From 6ffac0cca9154730a5ac5ddaf7e8408e098b5f16 Mon Sep 17 00:00:00 2001 From: "andres.robles" Date: Fri, 10 Jan 2020 12:57:05 -0600 Subject: [PATCH 1/5] Adding mockito plugin to be able to mock final classes Increasing test coverage for ObjectSerializer Fixing bug in GRPC Adapter while creating the envelopes, found during unit testing. --- .../io/dapr/client/DaprClientGrpcAdapter.java | 155 ++++--------- .../client/DaprClientGrpcAdapterTest.java | 118 ++++++++++ .../io/dapr/utils/ObjectSerializerTest.java | 218 +++++++++++++++--- .../org.mockito.plugins.MockMaker | 1 + 4 files changed, 351 insertions(+), 141 deletions(-) create mode 100644 sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java create mode 100644 sdk/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index a0eea02553..bd5f4d0c98 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -6,6 +6,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.Any; +import com.google.protobuf.ByteString; import com.google.protobuf.Empty; import io.dapr.DaprGrpc; import io.dapr.DaprProtos; @@ -15,6 +16,7 @@ import io.dapr.utils.ObjectSerializer; import reactor.core.publisher.Mono; +import java.io.IOException; import java.lang.reflect.Field; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -64,15 +66,12 @@ public Mono publishEvent(String topic, T event) { @Override public Mono publishEvent(String topic, T event, Map metadata) { try { - String serializedEvent = objectSerializer.serializeString(event); - Map mapEvent = new HashMap<>(); - mapEvent.put("Topic", topic); - mapEvent.put("Data", serializedEvent); + byte[] byteEvent = objectSerializer.serialize(event); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteEvent)).build(); // TODO: handle metadata. - byte[] byteEvent = objectSerializer.serialize(mapEvent); - - DaprProtos.PublishEventEnvelope envelope = DaprProtos.PublishEventEnvelope.parseFrom(byteEvent); + DaprProtos.PublishEventEnvelope envelope = DaprProtos.PublishEventEnvelope.newBuilder() + .setTopic(topic).setData(data).build(); ListenableFuture futureEmpty = client.publishEvent(envelope); return Mono.just(futureEmpty).flatMap(f -> { try { @@ -93,13 +92,7 @@ public Mono publishEvent(String topic, T event, Map me @Override public Mono invokeService(Verb verb, String appId, String method, R request, Map metadata, Class clazz) { try { - DaprProtos.InvokeServiceEnvelope.Builder envelopeBuilder = DaprProtos.InvokeServiceEnvelope.newBuilder(); - envelopeBuilder.setId(appId); - envelopeBuilder.setMethod(verb.toString()); - envelopeBuilder.setData(Any.parseFrom(objectSerializer.serialize(request))); - envelopeBuilder.getMetadataMap().putAll(metadata); - - DaprProtos.InvokeServiceEnvelope envelope = envelopeBuilder.build(); + DaprProtos.InvokeServiceEnvelope envelope = getInvodeServceEnvelope(verb.toString(), appId, method, request); ListenableFuture futureResponse = client.invokeService(envelope); return Mono.just(futureResponse).flatMap(f -> { @@ -153,11 +146,12 @@ public Mono invokeService(Verb verb, String appId, String method, byte[] @Override public Mono invokeBinding(String name, T request) { try { - Map mapMessage = new HashMap<>(); - mapMessage.put("Name", name); - mapMessage.put("Data", objectSerializer.serializeString(request)); - DaprProtos.InvokeBindingEnvelope envelope = - DaprProtos.InvokeBindingEnvelope.parseFrom(objectSerializer.serialize(mapMessage)); + byte[] byteRequest = objectSerializer.serialize(request); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteRequest)).build(); + DaprProtos.InvokeBindingEnvelope.Builder builder = DaprProtos.InvokeBindingEnvelope.newBuilder() + .setName(name) + .setData(data); + DaprProtos.InvokeBindingEnvelope envelope = builder.build(); ListenableFuture futureEmpty = client.invokeBinding(envelope); return Mono.just(futureEmpty).flatMap(f -> { try { @@ -176,13 +170,12 @@ public Mono invokeBinding(String name, T request) { * {@inheritDoc} */ @Override - public Mono getState(StateKeyValue key, StateOptions stateOptions, Class clazz) { + public Mono getState(StateKeyValue key, StateOptions stateOptions, Class clazz) { try { - Map request = new HashMap<>(); - request.put("Key", key.getKey()); - request.put("Consistency", stateOptions.getConsistency()); - byte[] serializedRequest = objectSerializer.serialize(request); - DaprProtos.GetStateEnvelope envelope = DaprProtos.GetStateEnvelope.parseFrom(serializedRequest); + DaprProtos.GetStateEnvelope.Builder builder = DaprProtos.GetStateEnvelope.newBuilder() + .setKey(key.getKey()) + .setConsistency(stateOptions.getConsistency()); + DaprProtos.GetStateEnvelope envelope = builder.build(); ListenableFuture futureResponse = client.getState(envelope); return Mono.just(futureResponse).flatMap(f -> { try { @@ -202,16 +195,21 @@ public Mono getState(StateKeyValue key, StateOptions stateOptions, C @Override public Mono saveStates(List> states, StateOptions options) { try { - List> listStates = new ArrayList<>(); - Map mapOptions = transformStateOptionsToMap(options); + DaprProtos.StateRequestOptions.Builder optionBuilder = DaprProtos.StateRequestOptions.newBuilder() + .setConsistency(options.getConsistency()); + DaprProtos.SaveStateEnvelope.Builder builder = DaprProtos.SaveStateEnvelope.newBuilder(); for (StateKeyValue state : states) { - Map mapState = transformStateKeyValueToMap(state, mapOptions); - listStates.add(mapState); - }; - Map mapStates = new HashMap<>(); - mapStates.put("Requests", listStates); - byte[] byteRequests = objectSerializer.serialize(mapStates); - DaprProtos.SaveStateEnvelope envelope = DaprProtos.SaveStateEnvelope.parseFrom(byteRequests); + byte[] byteState = objectSerializer.serialize(state.getValue()); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteState)).build(); + DaprProtos.StateRequest.Builder stateBuilder = DaprProtos.StateRequest.newBuilder() + .setEtag(state.getEtag()) + .setKey(state.getKey()) + .setValue(data); + stateBuilder.setOptions(optionBuilder.build()); + builder.addRequests(stateBuilder.build()); + } + DaprProtos.SaveStateEnvelope envelope = builder.build(); + ListenableFuture futureEmpty = client.saveState(envelope); return Mono.just(futureEmpty).flatMap(f -> { try { @@ -226,9 +224,6 @@ public Mono saveStates(List> states, StateOptions opt } } - /** - * {@inheritDoc} - */ @Override public Mono saveState(String key, String etag, T value, StateOptions options) { StateKeyValue state = new StateKeyValue<>(value, key, etag); @@ -242,10 +237,13 @@ public Mono saveState(String key, String etag, T value, StateOptions o @Override public Mono deleteState(StateKeyValue state, StateOptions options) { try { - Map mapOptions = transformStateOptionsToMap(options); - Map mapState = transformStateKeyValueToMap(state, mapOptions); - byte[] serializedState = objectSerializer.serialize(mapState); - DaprProtos.DeleteStateEnvelope envelope = DaprProtos.DeleteStateEnvelope.parseFrom(serializedState); + DaprProtos.StateOptions.Builder stateOptions = DaprProtos.StateOptions.newBuilder() + .setConsistency(options.getConsistency()); + DaprProtos.DeleteStateEnvelope.Builder builder = DaprProtos.DeleteStateEnvelope.newBuilder() + .setOptions(stateOptions) + .setEtag(state.getEtag()) + .setKey(state.getKey()); + DaprProtos.DeleteStateEnvelope envelope = builder.build(); ListenableFuture futureEmpty = client.deleteState(envelope); return Mono.just(futureEmpty).flatMap(f -> { try { @@ -262,6 +260,7 @@ public Mono deleteState(StateKeyValue state, StateOptions options) /** * Operation not supported for GRPC + * * @throws UnsupportedOperationException every time is called. */ @Override @@ -269,101 +268,45 @@ public Mono invokeActorMethod(String actorType, String actorId, String m return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono getActorState(String actorType, String actorId, String keyName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono saveActorStateTransactionally(String actorType, String actorId, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono registerActorReminder(String actorType, String actorId, String reminderName, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono unregisterActorReminder(String actorType, String actorId, String reminderName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono registerActorTimer(String actorType, String actorId, String timerName, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Operation not supported for GRPC - * @throws UnsupportedOperationException every time is called. - */ @Override public Mono unregisterActorTimer(String actorType, String actorId, String timerName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - /** - * Converts state options to map. - * - * TODO: Move this logic to StateOptions. - * @param options Instance to have is methods converted into map. - * @return Map for the state options. - * @throws IllegalAccessException Cannot extract params. - */ - private Map transformStateOptionsToMap(StateOptions options) - throws IllegalAccessException { - Map mapOptions = null; - if (options != null) { - mapOptions = new HashMap<>(); - for (Field field : options.getClass().getFields()) { - Object fieldValue = field.get(options); - if (fieldValue != null) { - mapOptions.put(field.getName(), fieldValue); - } - } - } - return mapOptions; + private DaprProtos.InvokeServiceEnvelope getInvodeServceEnvelope( + String verb, String appId, String method, K request) throws IOException { + byte[] byteRequest = objectSerializer.serialize(request); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteRequest)).build(); + DaprProtos.InvokeServiceEnvelope.Builder envelopeBuilder = DaprProtos.InvokeServiceEnvelope.newBuilder() + .setId(appId) + .setMethod(verb) + .setData(data); + return envelopeBuilder.build(); } - /** - * Creates an map for the given key-value operation. - * - * // TODO: Move this logic into StateKeyValue. - * @param state Key value for the state change. - * @param mapOptions Options to be applied to this operation. - * @return Map for the key-value operation. - * @throws IllegalAccessException Cannot identify key-value attributes. - */ - private Map transformStateKeyValueToMap(StateKeyValue state, Map mapOptions) - throws IllegalAccessException { - Map mapState = new HashMap<>(); - for (Field field : state.getClass().getFields()) { - mapState.put(field.getName(), field.get(state)); - } - if (mapOptions != null && !mapOptions.isEmpty()) { - mapState.put("Options", mapOptions); - } - return mapState; - } } \ No newline at end of file diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java new file mode 100644 index 0000000000..e154c2636e --- /dev/null +++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java @@ -0,0 +1,118 @@ +package io.dapr.client; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListenableFutureTask; +import com.google.common.util.concurrent.SettableFuture; +import com.google.protobuf.Empty; +import io.dapr.DaprGrpc; +import io.dapr.DaprProtos; +import org.checkerframework.checker.nullness.compatqual.NullableDecl; +import org.junit.Before; +import org.junit.Test; +import reactor.core.publisher.Mono; + +import javax.annotation.Nullable; +import java.util.Map; + +import static com.google.common.util.concurrent.Futures.addCallback; +import static com.google.common.util.concurrent.MoreExecutors.directExecutor; +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class DaprClientGrpcAdapterTest { + + private DaprGrpc.DaprFutureStub client; + private DaprClientGrpcAdapter adater; + + @Before + public void setup() { + client = mock(DaprGrpc.DaprFutureStub.class); + adater = new DaprClientGrpcAdapter(client); + } + + @Test(expected = UnsupportedOperationException.class) + public void unregisterActorTimerTest() { + Mono result = adater.unregisterActorTimer("actorType", "actorId", "timerName"); + result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void registerActorTimerTest() { + Mono result = adater.registerActorTimer("actorType", "actorId", "timerName" , "DATA"); + result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void unregisterActorReminderTest() { + Mono result = adater.unregisterActorReminder("actorType", "actorId", "reminderName"); + result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void registerActorReminderTest() { + Mono result = adater.registerActorReminder("actorType", "actorId", "reminderName", "DATA"); + result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void saveActorStateTransactionallyTest() { + Mono result = adater.saveActorStateTransactionally("actorType", "actorId", "DATA"); + result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void getActorStateTest() { + Mono result = adater.getActorState("actorType", "actorId", "keyName"); + String state = result.block(); + } + + @Test(expected = UnsupportedOperationException.class) + public void invokeActorMethodTest() { + Mono result = adater.invokeActorMethod("actorType", "actorId", "methodName", "jsonPlayload"); + String monoResult = result.block(); + } + + @Test + public void publishEventTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(Empty.newBuilder().build()); + when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.publishEvent("topic", "object"); + result.block(); + } + + private final class MockCallback implements FutureCallback { + @Nullable + private T value = null; + @Nullable + private Throwable failure = null; + private boolean wasCalled = false; + + public MockCallback(T expectedValue) { + this.value = expectedValue; + } + + public MockCallback(Throwable expectedFailure) { + this.failure = expectedFailure; + } + + @Override + public synchronized void onSuccess(@NullableDecl T result) { + assertFalse(wasCalled); + wasCalled = true; + assertEquals(value, result); + } + + @Override + public synchronized void onFailure(Throwable throwable) { + assertFalse(wasCalled); + wasCalled = true; + assertEquals(failure, throwable); + } + } +} diff --git a/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java b/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java index e261ca1b1c..ae20dc1e88 100644 --- a/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java +++ b/sdk/src/test/java/io/dapr/utils/ObjectSerializerTest.java @@ -174,13 +174,13 @@ public String toString() { } @Test - public void serializeObjectTest() { + public void serializeStringObjectTest() { MyObjectTestToSerialize obj = new MyObjectTestToSerialize(); obj.setStringValue("A String"); obj.setIntValue(2147483647); obj.setBoolValue(true); obj.setCharValue('a'); - obj.setByteValue((byte)65); + obj.setByteValue((byte) 65); obj.setShortValue((short) 32767); obj.setLongValue(9223372036854775807L); obj.setFloatValue(1.0f); @@ -197,13 +197,42 @@ public void serializeObjectTest() { } } + @Test + public void serializeObjectTest() { + MyObjectTestToSerialize obj = new MyObjectTestToSerialize(); + obj.setStringValue("A String"); + obj.setIntValue(2147483647); + obj.setBoolValue(true); + obj.setCharValue('a'); + obj.setByteValue((byte) 65); + obj.setShortValue((short) 32767); + obj.setLongValue(9223372036854775807L); + obj.setFloatValue(1.0f); + obj.setDoubleValue(1000.0); + //String expectedResult = "{\"stringValue\":\"A String\",\"intValue\":2147483647,\"boolValue\":true,\"charValue\":\"a\",\"byteValue\":65,\"shortValue\":32767,\"longValue\":9223372036854775807,\"floatValue\":1.0,\"doubleValue\":1000.0}"; + + ObjectSerializer serializer = new ObjectSerializer(); + byte[] serializedValue; + try { + serializedValue = serializer.serialize(obj); + assertNotNull(serializedValue); + MyObjectTestToSerialize deserializedValue = serializer.deserialize(serializedValue, MyObjectTestToSerialize.class); + assertEquals(obj, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + @Test public void serializeNullTest() { ObjectSerializer serializer = new ObjectSerializer(); String serializedValue; + byte[] byteSerializedValue; try { serializedValue = serializer.serializeString(null); - assertNull("The expected result is null", serializedValue); + assertNull(serializedValue); + byteSerializedValue = serializer.serialize(null); + assertNull(byteSerializedValue); } catch (IOException exception) { fail(exception.getMessage()); } @@ -214,9 +243,14 @@ public void serializeStringTest() { String valueToSerialize = "A String"; ObjectSerializer serializer = new ObjectSerializer(); String serializedValue; + byte [] byteValue; try { serializedValue = serializer.serializeString(valueToSerialize); assertEquals(valueToSerialize, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + String deserializedValue = serializer.deserialize(byteValue, String.class); + assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); } @@ -228,9 +262,109 @@ public void serializeIntTest() { String expectedResult = valueToSerialize.toString(); ObjectSerializer serializer = new ObjectSerializer(); String serializedValue; + byte [] byteValue; try { serializedValue = serializer.serializeString(valueToSerialize.intValue()); assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Integer deserializedValue = serializer.deserialize(byteValue, Integer.class); + assertEquals(valueToSerialize, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test + public void serializeShortTest() { + Short valueToSerialize = 1; + String expectedResult = valueToSerialize.toString(); + ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; + byte [] byteValue; + try { + serializedValue = serializer.serializeString(valueToSerialize.shortValue()); + assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Short deserializedValue = serializer.deserialize(byteValue, Short.class); + assertEquals(valueToSerialize, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test + public void serializeLongTest() { + Long valueToSerialize = 1L; + String expectedResult = valueToSerialize.toString(); + ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; + byte [] byteValue; + try { + serializedValue = serializer.serializeString(valueToSerialize.longValue()); + assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Long deserializedValue = serializer.deserialize(byteValue, Long.class); + assertEquals(valueToSerialize, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test + public void serializeFloatTest() { + Float valueToSerialize = 1.0f; + String expectedResult = valueToSerialize.toString(); + ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; + byte [] byteValue; + try { + serializedValue = serializer.serializeString(valueToSerialize.floatValue()); + assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Float deserializedValue = serializer.deserialize(byteValue, Float.class); + assertEquals(valueToSerialize, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test + public void serializeDoubleTest() { + Double valueToSerialize = 1.0; + String expectedResult = valueToSerialize.toString(); + ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; + byte [] byteValue; + try { + serializedValue = serializer.serializeString(valueToSerialize.doubleValue()); + assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Double deserializedValue = serializer.deserialize(byteValue, Double.class); + assertEquals(valueToSerialize, deserializedValue); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test + public void serializeBooleanTest() { + Boolean valueToSerialize = true; + String expectedResult = valueToSerialize.toString(); + ObjectSerializer serializer = new ObjectSerializer(); + String serializedValue; + byte [] byteValue; + try { + serializedValue = serializer.serializeString(valueToSerialize.booleanValue()); + assertEquals(expectedResult, serializedValue); + byteValue = serializer.serialize(valueToSerialize); + assertNotNull(byteValue); + Boolean deserializedValue = serializer.deserialize(byteValue, Boolean.class); + assertEquals(valueToSerialize, deserializedValue); } catch (IOException exception) { fail(exception.getMessage()); } @@ -244,7 +378,7 @@ public void deserializeObjectTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); @@ -259,33 +393,47 @@ public void deserializeObjectTest() { } } + @Test + public void deserializeBtyesTest() { + ObjectSerializer serializer = new ObjectSerializer(); + try { + byte[] resultStr = serializer.deserialize("String", byte[].class); + assertNotNull(resultStr); + byte[] result = serializer.deserialize("String".getBytes(), byte[].class); + assertNotNull(result); + } catch (IOException exception) { + fail(exception.getMessage()); + } + } + + @Test public void deserializeNullObjectOrPrimitiveTest() { ObjectSerializer serializer = new ObjectSerializer(); try { MyObjectTestToSerialize expectedObj = null; - MyObjectTestToSerialize objResult = serializer.deserialize(null, MyObjectTestToSerialize.class); - assertEquals(expectedObj, objResult); - boolean expectedBoolResutl = false; - boolean boolResult = serializer.deserialize(null, boolean.class); - assertEquals(expectedBoolResutl, boolResult); - byte expectedByteResult = Byte.valueOf((byte) 0); - byte byteResult = serializer.deserialize(null, byte.class); - assertEquals(expectedByteResult, byteResult); - short expectedShortResult = (short) 0; - short shortResult = serializer.deserialize(null, short.class); - assertEquals(expectedShortResult, shortResult); - int expectedIntResult = 0; - int intResult = serializer.deserialize(null, int.class); - assertEquals(expectedIntResult, intResult); - long expectedLongResult = 0L; - long longResult = serializer.deserialize(null, long.class); - assertEquals(expectedLongResult, longResult); - float expectedFloatResult = 0f; - float floatResult = serializer.deserialize(null, float.class); - assertEquals(expectedFloatResult, floatResult); - double expectedDoubleResult = (double) 0; - double doubleResult = serializer.deserialize(null, double.class); - assertEquals(expectedDoubleResult, doubleResult); + MyObjectTestToSerialize objResult = serializer.deserialize(null, MyObjectTestToSerialize.class); + assertEquals(expectedObj, objResult); + boolean expectedBoolResutl = false; + boolean boolResult = serializer.deserialize(null, boolean.class); + assertEquals(expectedBoolResutl, boolResult); + byte expectedByteResult = Byte.valueOf((byte) 0); + byte byteResult = serializer.deserialize(null, byte.class); + assertEquals(expectedByteResult, byteResult); + short expectedShortResult = (short) 0; + short shortResult = serializer.deserialize(null, short.class); + assertEquals(expectedShortResult, shortResult); + int expectedIntResult = 0; + int intResult = serializer.deserialize(null, int.class); + assertEquals(expectedIntResult, intResult); + long expectedLongResult = 0L; + long longResult = serializer.deserialize(null, long.class); + assertEquals(expectedLongResult, longResult); + float expectedFloatResult = 0f; + float floatResult = serializer.deserialize(null, float.class); + assertEquals(expectedFloatResult, floatResult, 0.0f); + double expectedDoubleResult = (double) 0; + double doubleResult = serializer.deserialize(null, double.class); + assertEquals(expectedDoubleResult, doubleResult, 0.0); } catch (IOException exception) { fail(exception.getMessage()); } @@ -298,7 +446,7 @@ public void deserializeObjectMissingStringPropertyTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); @@ -320,7 +468,7 @@ public void deserializeObjectMissingIntTest() { expectedResult.setStringValue("A String"); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); @@ -342,7 +490,7 @@ public void deserializeObjectMissingBooleanTest() { expectedResult.setStringValue("A String"); expectedResult.setIntValue(2147483647); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); @@ -364,7 +512,7 @@ public void deserializeObjectMissingCharTest() { expectedResult.setStringValue("A String"); expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); @@ -409,7 +557,7 @@ public void deserializeObjectMissingShortTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); @@ -431,7 +579,7 @@ public void deserializeObjectMissingLongTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setFloatValue(1.0f); expectedResult.setDoubleValue(1000.0); @@ -453,7 +601,7 @@ public void deserializeObjectMissingFloatTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setDoubleValue(1000.0); @@ -475,7 +623,7 @@ public void deserializeObjectMissingDoubleTest() { expectedResult.setIntValue(2147483647); expectedResult.setBoolValue(true); expectedResult.setCharValue('a'); - expectedResult.setByteValue((byte)65); + expectedResult.setByteValue((byte) 65); expectedResult.setShortValue((short) 32767); expectedResult.setLongValue(9223372036854775807L); expectedResult.setFloatValue(1.0f); diff --git a/sdk/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/sdk/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..ca6ee9cea8 --- /dev/null +++ b/sdk/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline \ No newline at end of file From 7ce1f3c9d7636a40bab8e53e1c3162753a3ba262 Mon Sep 17 00:00:00 2001 From: "andres.robles" Date: Sat, 11 Jan 2020 01:42:37 -0600 Subject: [PATCH 2/5] First step into returning Http Headers as part of the response for the DaprClientHttpAdapter Updating State object to match the API. Fixing Broken Unit Tests and increasing coverage for DaprClientGrpcAdapter --- .../actors/runtime/ActorStateSerializer.java | 1 + .../io/dapr/client/DaprClientGrpcAdapter.java | 78 +++++- .../io/dapr/client/DaprClientHttpAdapter.java | 23 +- .../main/java/io/dapr/client/DaprHttp.java | 78 ++++-- .../io/dapr/client/domain/StateKeyValue.java | 32 +++ .../io/dapr/client/domain/StateOptions.java | 90 ++++++- .../java/io/dapr/utils/DurationUtils.java | 142 +++++++++++ .../client/DaprClientGrpcAdapterTest.java | 235 +++++++++++++++++- .../java/io/dapr/client/DaprHttpStub.java | 12 +- .../java/io/dapr/client/DaprHttpTest.java | 20 +- .../java/io/dapr/runtime/DaprRuntimeTest.java | 3 +- .../java/io/dapr/utils/DurationUtilsTest.java | 102 ++++++++ 12 files changed, 759 insertions(+), 57 deletions(-) create mode 100644 sdk/src/main/java/io/dapr/utils/DurationUtils.java create mode 100644 sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java index 7cf1657171..38cb87e2b4 100644 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java +++ b/sdk-actors/src/main/java/io/dapr/actors/runtime/ActorStateSerializer.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonNode; +import io.dapr.utils.DurationUtils; import io.dapr.utils.ObjectSerializer; import java.io.IOException; diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index bd5f4d0c98..418d5f4496 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -7,6 +7,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.Any; import com.google.protobuf.ByteString; +import com.google.protobuf.Duration; import com.google.protobuf.Empty; import io.dapr.DaprGrpc; import io.dapr.DaprProtos; @@ -97,7 +98,7 @@ public Mono invokeService(Verb verb, String appId, String method, R re client.invokeService(envelope); return Mono.just(futureResponse).flatMap(f -> { try { - return Mono.just(objectSerializer.deserialize(f.get().getData().toByteArray(), clazz)); + return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toByteArray(), clazz)); } catch (Exception ex) { return Mono.error(ex); } @@ -174,7 +175,7 @@ public Mono getState(StateKeyValue key, StateOptions stateOptions, try { DaprProtos.GetStateEnvelope.Builder builder = DaprProtos.GetStateEnvelope.newBuilder() .setKey(key.getKey()) - .setConsistency(stateOptions.getConsistency()); + .setConsistency(stateOptions.getConsistency().getValue()); DaprProtos.GetStateEnvelope envelope = builder.build(); ListenableFuture futureResponse = client.getState(envelope); return Mono.just(futureResponse).flatMap(f -> { @@ -195,8 +196,35 @@ public Mono getState(StateKeyValue key, StateOptions stateOptions, @Override public Mono saveStates(List> states, StateOptions options) { try { - DaprProtos.StateRequestOptions.Builder optionBuilder = DaprProtos.StateRequestOptions.newBuilder() - .setConsistency(options.getConsistency()); + DaprProtos.StateRequestOptions.Builder optionBuilder = null; + if (options != null) { + DaprProtos.StateRetryPolicy.Builder retryPolicyBuilder = null; + if (options.getRetryPolicy() != null) { + retryPolicyBuilder = DaprProtos.StateRetryPolicy.newBuilder(); + StateOptions.RetryPolicy retryPolicy = options.getRetryPolicy(); + if (options.getRetryPolicy().getInterval() != null) { + Duration.Builder durationBuilder = Duration.newBuilder() + .setNanos(retryPolicy.getInterval().getNano()) + .setSeconds(retryPolicy.getInterval().getSeconds()); + retryPolicyBuilder.setInterval(durationBuilder.build()); + } + retryPolicyBuilder.setThreshold(objectSerializer.deserialize(retryPolicy.getThreshold(), int.class)); + if (retryPolicy.getPattern() != null) { + retryPolicyBuilder.setPattern(retryPolicy.getPattern().getValue()); + } + } + + optionBuilder = DaprProtos.StateRequestOptions.newBuilder(); + if (options.getConcurrency() != null) { + optionBuilder.setConcurrency(options.getConcurrency().getValue()); + } + if (options.getConsistency() != null) { + optionBuilder.setConsistency(options.getConsistency().getValue()); + } + if (retryPolicyBuilder != null) { + optionBuilder.setRetryPolicy(retryPolicyBuilder.build()); + } + } DaprProtos.SaveStateEnvelope.Builder builder = DaprProtos.SaveStateEnvelope.newBuilder(); for (StateKeyValue state : states) { byte[] byteState = objectSerializer.serialize(state.getValue()); @@ -205,7 +233,9 @@ public Mono saveStates(List> states, StateOptions opt .setEtag(state.getEtag()) .setKey(state.getKey()) .setValue(data); - stateBuilder.setOptions(optionBuilder.build()); + if(optionBuilder != null) { + stateBuilder.setOptions(optionBuilder.build()); + } builder.addRequests(stateBuilder.build()); } DaprProtos.SaveStateEnvelope envelope = builder.build(); @@ -237,12 +267,44 @@ public Mono saveState(String key, String etag, T value, StateOptions o @Override public Mono deleteState(StateKeyValue state, StateOptions options) { try { - DaprProtos.StateOptions.Builder stateOptions = DaprProtos.StateOptions.newBuilder() - .setConsistency(options.getConsistency()); + DaprProtos.StateOptions.Builder optionBuilder = null; + + if (options != null) { + optionBuilder = DaprProtos.StateOptions.newBuilder(); + DaprProtos.RetryPolicy.Builder retryPolicyBuilder = null; + if (options.getRetryPolicy() != null) { + retryPolicyBuilder = DaprProtos.RetryPolicy.newBuilder(); + StateOptions.RetryPolicy retryPolicy = options.getRetryPolicy(); + if (options.getRetryPolicy().getInterval() != null) { + Duration.Builder durationBuilder = Duration.newBuilder() + .setNanos(retryPolicy.getInterval().getNano()) + .setSeconds(retryPolicy.getInterval().getSeconds()); + retryPolicyBuilder.setInterval(durationBuilder.build()); + } + retryPolicyBuilder.setThreshold(objectSerializer.deserialize(retryPolicy.getThreshold(), int.class)); + if (retryPolicy.getPattern() != null) { + retryPolicyBuilder.setPattern(retryPolicy.getPattern().getValue()); + } + } + + optionBuilder = DaprProtos.StateOptions.newBuilder(); + if (options.getConcurrency() != null) { + optionBuilder.setConcurrency(options.getConcurrency().getValue()); + } + if (options.getConsistency() != null) { + optionBuilder.setConsistency(options.getConsistency().getValue()); + } + if (retryPolicyBuilder != null) { + optionBuilder.setRetryPolicy(retryPolicyBuilder.build()); + } + } DaprProtos.DeleteStateEnvelope.Builder builder = DaprProtos.DeleteStateEnvelope.newBuilder() - .setOptions(stateOptions) .setEtag(state.getEtag()) .setKey(state.getKey()); + if (optionBuilder != null) { + builder.setOptions(optionBuilder.build()); + } + DaprProtos.DeleteStateEnvelope envelope = builder.build(); ListenableFuture futureEmpty = client.deleteState(envelope); return Mono.just(futureEmpty).flatMap(f -> { diff --git a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java index 621011cf39..c51550c561 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java @@ -93,10 +93,11 @@ public Mono invokeService(Verb verb, String appId, String method, R re } String path = String.format("%s/%s/method/%s", Constants.INVOKE_PATH, appId, method); byte[] serializedRequestBody = objectSerializer.serialize(request); - return this.client.invokeAPI(httMethod, path, serializedRequestBody, metadata) + Mono response = this.client.invokeAPI(httMethod, path, serializedRequestBody, metadata); + return Mono.just(response) .flatMap(r -> { try { - return Mono.just(objectSerializer.deserialize(r, clazz)); + return Mono.just(objectSerializer.deserialize(r.block().getBody(), clazz)); } catch (Exception ex) { return Mono.error(ex); } @@ -258,7 +259,14 @@ public Mono deleteState(StateKeyValue state, StateOptions options) @Override public Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload) { String url = String.format(Constants.ACTOR_METHOD_RELATIVE_URL_FORMAT, actorType, actorId, methodName); - return this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, jsonPayload, null); + Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, jsonPayload, null); + return Mono.just(responseMono).flatMap(f -> { + try { + return Mono.just(f.block().getBody()); + } catch (Exception ex) { + return Mono.error(ex); + } + }); } /** @@ -267,7 +275,14 @@ public Mono invokeActorMethod(String actorType, String actorId, String m @Override public Mono getActorState(String actorType, String actorId, String keyName) { String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); - return this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, "", null); + Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, "", null); + return Mono.just(responseMono).flatMap(f -> { + try { + return Mono.just(f.block().getBody()); + } catch (Exception ex) { + return Mono.error(ex); + } + }); } /** diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index 1c78474b5d..f5b617c981 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -8,16 +8,18 @@ import io.dapr.exceptions.DaprError; import io.dapr.exceptions.DaprException; import io.dapr.utils.Constants; -import okhttp3.*; +import io.dapr.utils.ObjectSerializer; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; import reactor.core.publisher.Mono; import java.io.IOException; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -29,6 +31,30 @@ class DaprHttp { */ enum HttpMethods { GET, PUT, POST, DELETE; } + static class Response { + private String body; + private Map headers; + private int statusCode; + + public Response(String body, Map headers, int statusCode) { + this.body = body; + this.headers = headers; + this.statusCode = statusCode; + } + + public String getBody() { + return body; + } + + public Map getHeaders() { + return headers; + } + + public int getStatusCode() { + return statusCode; + } + } + /** * Defines the standard application/json type for HTTP calls in Dapr. */ @@ -86,8 +112,8 @@ enum HttpMethods { GET, PUT, POST, DELETE; } * @param urlString url as String. * @return Asynchronous text */ - public Mono invokeAPI(String method, String urlString, Map headers) { - return this.invokeAPI(method, urlString, (String) null, headers); + public Mono invokeAPI(String method, String urlString, Map headers) { + return this.invokeAPI(method, urlString, (byte[])null, headers); } /** @@ -98,13 +124,8 @@ public Mono invokeAPI(String method, String urlString, Map invokeAPI(String method, String urlString, String content, Map headers) { - return this.invokeAPI( - method, - urlString, - content == null ? EMPTY_BYTES : content.getBytes(StandardCharsets.UTF_8), - headers) - .map(s -> new String(s, StandardCharsets.UTF_8)); + public Mono invokeAPI(String method, String urlString, String content, Map headers) { + return this.invokeAPI(method, urlString, content == null ? EMPTY_BYTES : content.getBytes(StandardCharsets.UTF_8), headers); } /** @@ -115,12 +136,12 @@ public Mono invokeAPI(String method, String urlString, String content, M * @param content payload to be posted. * @return Asynchronous text */ - public Mono invokeAPI(String method, String urlString, byte[] content, Map headers) { + public Mono invokeAPI(String method, String urlString, byte[] content, Map headers) { return Mono.fromFuture(CompletableFuture.supplyAsync( () -> { try { String requestId = UUID.randomUUID().toString(); - RequestBody body; + RequestBody body = REQUEST_BODY_EMPTY_JSON; String contentType = headers != null ? headers.get("content-type") : null; MediaType mediaType = contentType == null ? MEDIA_TYPE_APPLICATION_JSON : MediaType.get(contentType); @@ -150,17 +171,22 @@ public Mono invokeAPI(String method, String urlString, byte[] content, M Request request = requestBuilder.build(); - try (Response response = this.httpClient.newCall(request).execute()) { - byte[] responseBody = response.body().bytes(); + try (okhttp3.Response response = this.httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { - DaprError error = this.parseDaprError(responseBody); + DaprError error = parseDaprError(response.body().string()); if ((error != null) && (error.getErrorCode() != null) && (error.getMessage() != null)) { - throw new DaprException(error); + throw new RuntimeException(new DaprException(error)); } - throw new IOException("Unknown error."); + throw new RuntimeException("Unknown error."); } - return responseBody == null ? EMPTY_BYTES : responseBody; + + Map mapHeaders = new HashMap<>(); + String result = response.body().string(); + response.headers().forEach(pair -> { + mapHeaders.put(pair.getFirst(), pair.getSecond()); + }); + return new Response(result, mapHeaders, response.code()); } } catch (Exception e) { throw new RuntimeException(e); @@ -174,12 +200,16 @@ public Mono invokeAPI(String method, String urlString, byte[] content, M * @param json Response body from Dapr. * @return DaprError or null if could not parse. */ - private static DaprError parseDaprError(byte[] json) throws IOException { + private static DaprError parseDaprError(String json) { if (json == null) { return null; } - return OBJECT_MAPPER.readValue(json, DaprError.class); + try { + return OBJECT_MAPPER.readValue(json, DaprError.class); + } catch (IOException e) { + throw new DaprException("500", "Unknown error: could not parse error json."); + } } } diff --git a/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java b/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java index 599045907c..97b99a8a1b 100644 --- a/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java +++ b/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java @@ -4,25 +4,57 @@ */ package io.dapr.client.domain; +/** + * This class reprent what a State is + * @param + */ public class StateKeyValue { + /** + * The value of the state + */ private final T value; + /** + * The key of the state + */ private final String key; + /** + * The ETag to be used + * For REDIS ONLY this must be an integer + */ private final String etag; + /** + * Create an inmutable state + * @param value + * @param key + * @param etag + */ public StateKeyValue(T value, String key, String etag) { this.value = value; this.key = key; this.etag = etag; } + /** + * Retrieves the Value of the state + * @return + */ public T getValue() { return value; } + /** + * Retrieves the Key of the state + * @return + */ public String getKey() { return key; } + /** + * Retrieve the ETag of this state + * @return + */ public String getEtag() { return etag; } diff --git a/sdk/src/main/java/io/dapr/client/domain/StateOptions.java b/sdk/src/main/java/io/dapr/client/domain/StateOptions.java index 834baaf98a..0772916dd6 100644 --- a/sdk/src/main/java/io/dapr/client/domain/StateOptions.java +++ b/sdk/src/main/java/io/dapr/client/domain/StateOptions.java @@ -4,16 +4,98 @@ */ package io.dapr.client.domain; -public class StateOptions { +import java.time.Duration; - private final String consistency; +public class StateOptions { + private Consistency consistency; + private Concurrency concurrency; + private RetryPolicy retryPolicy; - public StateOptions(String consistency) { + public StateOptions(Consistency consistency, Concurrency concurrency, RetryPolicy retryPolicy) { this.consistency = consistency; + this.concurrency = concurrency; + this.retryPolicy = retryPolicy; + } + + public Concurrency getConcurrency() { + return concurrency; } - public String getConsistency() { + public Consistency getConsistency() { return consistency; } + public RetryPolicy getRetryPolicy() { + return retryPolicy; + } + + public static enum Consistency { + EVENTUAL("eventual"), + STRONG("strong"); + + private String value; + + private Consistency(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + + public static enum Concurrency { + FIRST_WRITE("first-write"), + LAST_WRITE ("last-write"); + + private String value; + + private Concurrency(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + + public static class RetryPolicy { + public static enum Pattern { + LINEAR("linear"), + EXPONENTIAL("exponential"); + + private String value; + + private Pattern(String value) { + this.value = value; + } + + public String getValue() { + return this.value; + } + } + + private Duration interval; + private String threshold; + private Pattern pattern; + + + public RetryPolicy(Duration interval, String threshold, Pattern pattern) { + this.interval = interval; + this.threshold = threshold; + this.pattern = pattern; + } + + public Duration getInterval() { + return interval; + } + + public String getThreshold() { + return threshold; + } + + public Pattern getPattern() { + return pattern; + } + } } diff --git a/sdk/src/main/java/io/dapr/utils/DurationUtils.java b/sdk/src/main/java/io/dapr/utils/DurationUtils.java new file mode 100644 index 0000000000..ddfffe2ae2 --- /dev/null +++ b/sdk/src/main/java/io/dapr/utils/DurationUtils.java @@ -0,0 +1,142 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// ------------------------------------------------------------ + +package io.dapr.utils; + +import java.time.Duration; + +public class DurationUtils { + + /** + * Converts time from the String format used by Dapr into a Duration. + * + * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). + * @return A Duration + */ + public static Duration ConvertDurationFromDaprFormat(String valueString) { + // Convert the format returned by the Dapr runtime into Duration + // An example of the format is: 4h15m50s60ms. It does not include days. + int hIndex = valueString.indexOf('h'); + int mIndex = valueString.indexOf('m'); + int sIndex = valueString.indexOf('s'); + int msIndex = valueString.indexOf("ms"); + + String hoursSpan = valueString.substring(0, hIndex); + + int hours = Integer.parseInt(hoursSpan); + int days = hours / 24; + hours = hours % 24; + + String minutesSpan = valueString.substring(hIndex + 1, mIndex); + int minutes = Integer.parseInt(minutesSpan); + + String secondsSpan = valueString.substring(mIndex + 1, sIndex); + int seconds = Integer.parseInt(secondsSpan); + + String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); + int milliseconds = Integer.parseInt(millisecondsSpan); + + return Duration.ZERO + .plusDays(days) + .plusHours(hours) + .plusMinutes(minutes) + .plusSeconds(seconds) + .plusMillis(milliseconds); + } + + /** + * Converts a Duration to the format used by the Dapr runtime. + * + * @param value Duration + * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) + */ + public static String ConvertDurationToDaprFormat(Duration value) { + String stringValue = ""; + + // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A + // negative "period" means fire once only. + if (value == Duration.ZERO || + (value.compareTo(Duration.ZERO) == 1)) { + long hours = getDaysPart(value) * 24 + getHoursPart(value); + + StringBuilder sb = new StringBuilder(); + + sb.append(hours); + sb.append("h"); + + sb.append(getMinutesPart((value))); + sb.append("m"); + + sb.append(getSecondsPart((value))); + sb.append("s"); + + sb.append(getMilliSecondsPart((value))); + sb.append("ms"); + + return sb.toString(); + } + + return stringValue; + } + + /** + * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. + * + * @param d Duration + * @return Number of days. + */ + static long getDaysPart(Duration d) { + long t = d.getSeconds() / 60 / 60 / 24; + return t; + } + + /** + * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. + * + * @param d The duration to parse + * @return the hour part of the duration + */ + static long getHoursPart(Duration d) { + long u = (d.getSeconds() / 60 / 60) % 24; + + return u; + } + + /** + * Helper to get the "minutes" part of the Duration. + * + * @param d The duration to parse + * @return the minutes part of the duration + */ + static long getMinutesPart(Duration d) { + long u = (d.getSeconds() / 60) % 60; + + return u; + } + + /** + * Helper to get the "seconds" part of the Duration. + * + * @param d The duration to parse + * @return the seconds part of the duration + */ + static long getSecondsPart(Duration d) { + long u = d.getSeconds() % 60; + + return u; + } + + /** + * Helper to get the "millis" part of the Duration. + * + * @param d The duration to parse + * @return the milliseconds part of the duration + */ + static long getMilliSecondsPart(Duration d) { + long u = d.toMillis() % 1000; + + return u; + } +} diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java index e154c2636e..cae3e4a1dd 100644 --- a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java @@ -1,19 +1,22 @@ package io.dapr.client; import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.ListenableFutureTask; import com.google.common.util.concurrent.SettableFuture; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; import com.google.protobuf.Empty; import io.dapr.DaprGrpc; import io.dapr.DaprProtos; +import io.dapr.client.domain.Verb; +import io.dapr.utils.ObjectSerializer; import org.checkerframework.checker.nullness.compatqual.NullableDecl; import org.junit.Before; import org.junit.Test; import reactor.core.publisher.Mono; import javax.annotation.Nullable; -import java.util.Map; + +import java.io.IOException; import static com.google.common.util.concurrent.Futures.addCallback; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; @@ -25,11 +28,13 @@ public class DaprClientGrpcAdapterTest { private DaprGrpc.DaprFutureStub client; private DaprClientGrpcAdapter adater; + private ObjectSerializer serializer; @Before public void setup() { client = mock(DaprGrpc.DaprFutureStub.class); adater = new DaprClientGrpcAdapter(client); + serializer = new ObjectSerializer(); } @Test(expected = UnsupportedOperationException.class) @@ -74,6 +79,27 @@ public void invokeActorMethodTest() { String monoResult = result.block(); } + @Test(expected = RuntimeException.class) + public void publishEventExceptionThrownTest() { + when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.publishEvent("topic", "object"); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void publishEventCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.setException(ex); + when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.publishEvent("topic", "object"); + result.block(); + } + @Test public void publishEventTest() { SettableFuture settableFuture = SettableFuture.create(); @@ -86,6 +112,180 @@ public void publishEventTest() { result.block(); } + @Test + public void publishEventObjectTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(Empty.newBuilder().build()); + when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) + .thenReturn(settableFuture); + MyObject event = new MyObject(1, "Event"); + Mono result = adater.publishEvent("topic", event); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceVoidExceptionThrownTest() { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceVoidCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.setException(ex); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + result.block(); + } + + @Test + public void invokeServiceVoidTest() throws Exception { + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny("Value")).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + result.block(); + } + + @Test + public void invokeServiceVoidObjectTest() throws Exception { + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny("Value")).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + MyObject request = new MyObject(1, "Event"); + Mono result = adater.invokeService(Verb.GET, "appId", "method", request, null); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceExceptionThrownTest() { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.setException(ex); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + result.block(); + } + + @Test + public void invokeServiceTest() throws Exception { + String expected = "Value"; + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(expected)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + String strOutput = result.block(); + assertEquals(expected, strOutput); + } + + @Test + public void invokeServiceObjectTest() throws Exception { + MyObject resultObj = new MyObject(1, "Value"); + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(resultObj)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + String strOutput = result.block(); + assertEquals(serializer.serializeString(resultObj), strOutput); + } + + @Test(expected = RuntimeException.class) + public void invokeBindingExceptionThrownTest() { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.invokeBinding("BindingName", "request"); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeBindingCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.setException(ex); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeBinding("BindingName", "request"); + result.block(); + } + + @Test + public void invokeBindingTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(Empty.newBuilder().build()); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeBinding("BindingName", "request"); + result.block(); + } + + @Test + public void invokeBindingObjectTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(Empty.newBuilder().build()); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + MyObject event = new MyObject(1, "Event"); + Mono result = adater.invokeBinding("BindingName", event); + result.block(); + } + + private Any getAny(T value) throws IOException { + byte[] byteValue = serializer.serialize(value); + return Any.newBuilder().setValue(ByteString.copyFrom(byteValue)).build(); + } + private final class MockCallback implements FutureCallback { @Nullable private T value = null; @@ -115,4 +315,33 @@ public synchronized void onFailure(Throwable throwable) { assertEquals(failure, throwable); } } + + public static class MyObject { + private Integer id; + private String value; + + public MyObject() { + } + + public MyObject(Integer id, String value) { + this.id = id; + this.value = value; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } } diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpStub.java b/sdk/src/test/java/io/dapr/client/DaprHttpStub.java index 391c14ddfa..7c3a2eb7ca 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpStub.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpStub.java @@ -15,6 +15,11 @@ */ public class DaprHttpStub extends DaprHttp { + public static class ResponseStub extends DaprHttp.Response { + public ResponseStub(String body, Map headers, int statusCode) { + super(body, headers, statusCode); + } + } /** * Instantiates a stub for DaprHttp */ @@ -24,9 +29,10 @@ public DaprHttpStub() { /** * {@inheritDoc} + * @return */ @Override - public Mono invokeAPI(String method, String urlString, Map headers) { + public Mono invokeAPI(String method, String urlString, Map headers) { return Mono.empty(); } @@ -34,7 +40,7 @@ public Mono invokeAPI(String method, String urlString, Map invokeAPI(String method, String urlString, String content, Map headers) { + public Mono invokeAPI(String method, String urlString, String content, Map headers) { return Mono.empty(); } @@ -42,7 +48,7 @@ public Mono invokeAPI(String method, String urlString, String content, M * {@inheritDoc} */ @Override - public Mono invokeAPI(String method, String urlString, byte[] content, Map headers) { + public Mono invokeAPI(String method, String urlString, byte[] content, Map headers) { return Mono.empty(); } } diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java index 000fc01336..3baa82409d 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java @@ -41,8 +41,8 @@ public void invokePostMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); - Mono mono = daprHttp.invokeAPI("POST","v1.0/state",null); - assertEquals(EXPECTED_RESULT,mono.block()); + Mono mono = daprHttp.invokeAPI("POST","v1.0/state",null); + assertEquals(EXPECTED_RESULT,mono.block().getBody()); } @@ -55,8 +55,8 @@ public void invokeDeleteMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); - Mono mono = daprHttp.invokeAPI("DELETE","v1.0/state",null); - assertEquals(EXPECTED_RESULT,mono.block()); + Mono mono = daprHttp.invokeAPI("DELETE","v1.0/state",null); + assertEquals(EXPECTED_RESULT,mono.block().getBody()); } @@ -69,9 +69,9 @@ public void invokeGetMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); - Mono mono = daprHttp.invokeAPI("GET","v1.0/get",null); + Mono mono = daprHttp.invokeAPI("GET","v1.0/get",null); - assertEquals(EXPECTED_RESULT,mono.block()); + assertEquals(EXPECTED_RESULT,mono.block().getBody()); } @@ -87,9 +87,9 @@ public void invokeMethodWithHeaders() { .respond(EXPECTED_RESULT); DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); - Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); + Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); - assertEquals(EXPECTED_RESULT,mono.block()); + assertEquals(EXPECTED_RESULT,mono.block().getBody()); } @@ -107,9 +107,9 @@ public void invokeMethodRuntimeException(){ DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); - Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); + Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); - assertEquals(EXPECTED_RESULT,mono.block()); + assertEquals(EXPECTED_RESULT,mono.block().getBody()); } } \ No newline at end of file diff --git a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java index 5c4e6c5d27..9146cc4e6b 100644 --- a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java +++ b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java @@ -204,7 +204,8 @@ public void invokeHappyCase() throws Exception { this.daprRuntime.handleInvocation( METHOD_NAME, message.data, - message.metadata)); + message.metadata) + .map(r -> new DaprHttpStub.ResponseStub(new String(r, StandardCharsets.UTF_8), null, 200))); Mono response = client.invokeService(Verb.POST, APP_ID, METHOD_NAME, message.data, message.metadata); Assert.assertEquals( diff --git a/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java b/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java new file mode 100644 index 0000000000..cf3bc057c9 --- /dev/null +++ b/sdk/src/test/java/io/dapr/utils/DurationUtilsTest.java @@ -0,0 +1,102 @@ +package io.dapr.utils; + +import io.dapr.utils.DurationUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.time.Duration; + +public class DurationUtilsTest { + + @Test + public void convertTimeBothWays() { + String s = "4h15m50s60ms"; + Duration d1 = DurationUtils.ConvertDurationFromDaprFormat(s); + + String t = DurationUtils.ConvertDurationToDaprFormat(d1); + Assert.assertEquals(s, t); + } + + @Test + public void largeHours() { + // hours part is larger than 24 + String s = "31h15m50s60ms"; + Duration d1 = DurationUtils.ConvertDurationFromDaprFormat(s); + + String t = DurationUtils.ConvertDurationToDaprFormat(d1); + Assert.assertEquals(s, t); + } + + @Test + public void negativeDuration() { + Duration d = Duration.ofSeconds(-99); + String t = DurationUtils.ConvertDurationToDaprFormat(d); + Assert.assertEquals("", t); + } + + @Test + public void testGetHoursPart() { + Duration d1 = Duration.ZERO.plusHours(26); + Assert.assertEquals(2, DurationUtils.getHoursPart(d1)); + + Duration d2 = Duration.ZERO.plusHours(23); + Assert.assertEquals(23, DurationUtils.getHoursPart(d2)); + + Duration d3 = Duration.ZERO.plusHours(24); + Assert.assertEquals(0, DurationUtils.getHoursPart(d3)); + } + + @Test + public void testGetMinutesPart() { + Duration d1 = Duration.ZERO.plusMinutes(61); + Assert.assertEquals(1, DurationUtils.getMinutesPart(d1)); + + Duration d2 = Duration.ZERO.plusMinutes(60); + Assert.assertEquals(0, DurationUtils.getMinutesPart(d2)); + + Duration d3 = Duration.ZERO.plusMinutes(59); + Assert.assertEquals(59, DurationUtils.getMinutesPart(d3)); + + Duration d4 = Duration.ZERO.plusMinutes(3600); + Assert.assertEquals(0, DurationUtils.getMinutesPart(d4)); + } + + @Test + public void testGetSecondsPart() { + Duration d1 = Duration.ZERO.plusSeconds(61); + Assert.assertEquals(1, DurationUtils.getSecondsPart(d1)); + + Duration d2 = Duration.ZERO.plusSeconds(60); + Assert.assertEquals(0, DurationUtils.getSecondsPart(d2)); + + Duration d3 = Duration.ZERO.plusSeconds(59); + Assert.assertEquals(59, DurationUtils.getSecondsPart(d3)); + + Duration d4 = Duration.ZERO.plusSeconds(3600); + Assert.assertEquals(0, DurationUtils.getSecondsPart(d4)); + } + + @Test + public void testGetMillisecondsPart() { + Duration d1 = Duration.ZERO.plusMillis(61); + Assert.assertEquals(61, DurationUtils.getMilliSecondsPart(d1)); + + Duration d2 = Duration.ZERO.plusMillis(60); + Assert.assertEquals(60, DurationUtils.getMilliSecondsPart(d2)); + + Duration d3 = Duration.ZERO.plusMillis(59); + Assert.assertEquals(59, DurationUtils.getMilliSecondsPart(d3)); + + Duration d4 = Duration.ZERO.plusMillis(999); + Assert.assertEquals(999, DurationUtils.getMilliSecondsPart(d4)); + + Duration d5 = Duration.ZERO.plusMillis(1001); + Assert.assertEquals(1, DurationUtils.getMilliSecondsPart(d5)); + + Duration d6 = Duration.ZERO.plusMillis(1000); + Assert.assertEquals(0, DurationUtils.getMilliSecondsPart(d6)); + + Duration d7 = Duration.ZERO.plusMillis(10000); + Assert.assertEquals(0, DurationUtils.getMilliSecondsPart(d7)); + } +} From 0ea90c2bfbab16b4004ca5611ed7ddd7bc6d1e91 Mon Sep 17 00:00:00 2001 From: "andres.robles" Date: Sat, 11 Jan 2020 01:47:11 -0600 Subject: [PATCH 3/5] Adding documentation, fixing typos and renaming support method to be more descriptive. --- .../io/dapr/client/DaprClientGrpcAdapter.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index 418d5f4496..1390870bf2 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -18,9 +18,7 @@ import reactor.core.publisher.Mono; import java.io.IOException; -import java.lang.reflect.Field; import java.util.*; -import java.util.concurrent.CompletableFuture; /** * An adapter for the GRPC Client. @@ -93,7 +91,7 @@ public Mono publishEvent(String topic, T event, Map me @Override public Mono invokeService(Verb verb, String appId, String method, R request, Map metadata, Class clazz) { try { - DaprProtos.InvokeServiceEnvelope envelope = getInvodeServceEnvelope(verb.toString(), appId, method, request); + DaprProtos.InvokeServiceEnvelope envelope = buildInvokeServiceEnvelope(verb.toString(), appId, method, request); ListenableFuture futureResponse = client.invokeService(envelope); return Mono.just(futureResponse).flatMap(f -> { @@ -330,37 +328,77 @@ public Mono invokeActorMethod(String actorType, String actorId, String m return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono getActorState(String actorType, String actorId, String keyName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono saveActorStateTransactionally(String actorType, String actorId, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono registerActorReminder(String actorType, String actorId, String reminderName, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono unregisterActorReminder(String actorType, String actorId, String reminderName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono registerActorTimer(String actorType, String actorId, String timerName, String data) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } + /** + * Operation not supported for GRPC + * + * @throws UnsupportedOperationException every time is called. + */ @Override public Mono unregisterActorTimer(String actorType, String actorId, String timerName) { return Mono.error(new UnsupportedOperationException("Operation not supported for GRPC")); } - private DaprProtos.InvokeServiceEnvelope getInvodeServceEnvelope( + /** + * Builds the object io.dapr.{@link DaprProtos.InvokeServiceEnvelope} to be send based on the parameters. + * @param verb + * @param appId + * @param method + * @param request + * @param + * @return + * @throws IOException + */ + private DaprProtos.InvokeServiceEnvelope buildInvokeServiceEnvelope( String verb, String appId, String method, K request) throws IOException { byte[] byteRequest = objectSerializer.serialize(request); Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteRequest)).build(); From 8bbf4fa92b9227708825cc7e381fda4b9b4905ae Mon Sep 17 00:00:00 2001 From: "andres.robles" Date: Sun, 12 Jan 2020 20:00:41 -0600 Subject: [PATCH 4/5] Addressing PR comments Increasing test coverage Fixing Merge conflicts --- .../io/dapr/actors/runtime/DurationUtils.java | 142 -------- .../actors/runtime/DurationUtilsTest.java | 101 ------ .../main/java/io/dapr/client/DaprClient.java | 5 +- .../io/dapr/client/DaprClientGrpcAdapter.java | 45 ++- .../io/dapr/client/DaprClientHttpAdapter.java | 30 +- .../main/java/io/dapr/client/DaprHttp.java | 8 +- .../io/dapr/client/domain/StateKeyValue.java | 33 +- .../io/dapr/client/domain/StateOptions.java | 16 +- .../java/io/dapr/utils/DurationUtils.java | 256 +++++++------- .../client/DaprClientGrpcAdapterTest.java | 321 ++++++++++++++++-- .../java/io/dapr/client/DaprHttpStub.java | 2 +- .../java/io/dapr/client/DaprHttpTest.java | 30 +- .../java/io/dapr/runtime/DaprRuntimeTest.java | 2 +- 13 files changed, 543 insertions(+), 448 deletions(-) delete mode 100644 sdk-actors/src/main/java/io/dapr/actors/runtime/DurationUtils.java delete mode 100644 sdk-actors/src/test/java/io/dapr/actors/runtime/DurationUtilsTest.java diff --git a/sdk-actors/src/main/java/io/dapr/actors/runtime/DurationUtils.java b/sdk-actors/src/main/java/io/dapr/actors/runtime/DurationUtils.java deleted file mode 100644 index f09a19573c..0000000000 --- a/sdk-actors/src/main/java/io/dapr/actors/runtime/DurationUtils.java +++ /dev/null @@ -1,142 +0,0 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. -// ------------------------------------------------------------ - -package io.dapr.actors.runtime; - -import java.time.Duration; - -public class DurationUtils { - - /** - * Converts time from the String format used by Dapr into a Duration. - * - * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). - * @return A Duration - */ - public static Duration ConvertDurationFromDaprFormat(String valueString) { - // Convert the format returned by the Dapr runtime into Duration - // An example of the format is: 4h15m50s60ms. It does not include days. - int hIndex = valueString.indexOf('h'); - int mIndex = valueString.indexOf('m'); - int sIndex = valueString.indexOf('s'); - int msIndex = valueString.indexOf("ms"); - - String hoursSpan = valueString.substring(0, hIndex); - - int hours = Integer.parseInt(hoursSpan); - int days = hours / 24; - hours = hours % 24; - - String minutesSpan = valueString.substring(hIndex + 1, mIndex); - int minutes = Integer.parseInt(minutesSpan); - - String secondsSpan = valueString.substring(mIndex + 1, sIndex); - int seconds = Integer.parseInt(secondsSpan); - - String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); - int milliseconds = Integer.parseInt(millisecondsSpan); - - return Duration.ZERO - .plusDays(days) - .plusHours(hours) - .plusMinutes(minutes) - .plusSeconds(seconds) - .plusMillis(milliseconds); - } - - /** - * Converts a Duration to the format used by the Dapr runtime. - * - * @param value Duration - * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) - */ - public static String ConvertDurationToDaprFormat(Duration value) { - String stringValue = ""; - - // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A - // negative "period" means fire once only. - if (value == Duration.ZERO || - (value.compareTo(Duration.ZERO) == 1)) { - long hours = getDaysPart(value) * 24 + getHoursPart(value); - - StringBuilder sb = new StringBuilder(); - - sb.append(hours); - sb.append("h"); - - sb.append(getMinutesPart((value))); - sb.append("m"); - - sb.append(getSecondsPart((value))); - sb.append("s"); - - sb.append(getMilliSecondsPart((value))); - sb.append("ms"); - - return sb.toString(); - } - - return stringValue; - } - - /** - * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. - * - * @param d Duration - * @return Number of days. - */ - static long getDaysPart(Duration d) { - long t = d.getSeconds() / 60 / 60 / 24; - return t; - } - - /** - * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. - * - * @param d The duration to parse - * @return the hour part of the duration - */ - static long getHoursPart(Duration d) { - long u = (d.getSeconds() / 60 / 60) % 24; - - return u; - } - - /** - * Helper to get the "minutes" part of the Duration. - * - * @param d The duration to parse - * @return the minutes part of the duration - */ - static long getMinutesPart(Duration d) { - long u = (d.getSeconds() / 60) % 60; - - return u; - } - - /** - * Helper to get the "seconds" part of the Duration. - * - * @param d The duration to parse - * @return the seconds part of the duration - */ - static long getSecondsPart(Duration d) { - long u = d.getSeconds() % 60; - - return u; - } - - /** - * Helper to get the "millis" part of the Duration. - * - * @param d The duration to parse - * @return the milliseconds part of the duration - */ - static long getMilliSecondsPart(Duration d) { - long u = d.toMillis() % 1000; - - return u; - } -} diff --git a/sdk-actors/src/test/java/io/dapr/actors/runtime/DurationUtilsTest.java b/sdk-actors/src/test/java/io/dapr/actors/runtime/DurationUtilsTest.java deleted file mode 100644 index 8b475eef4a..0000000000 --- a/sdk-actors/src/test/java/io/dapr/actors/runtime/DurationUtilsTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package io.dapr.actors.runtime; - -import org.junit.Assert; -import org.junit.Test; - -import java.time.Duration; - -public class DurationUtilsTest { - - @Test - public void convertTimeBothWays() { - String s = "4h15m50s60ms"; - Duration d1 = DurationUtils.ConvertDurationFromDaprFormat(s); - - String t = DurationUtils.ConvertDurationToDaprFormat(d1); - Assert.assertEquals(s, t); - } - - @Test - public void largeHours() { - // hours part is larger than 24 - String s = "31h15m50s60ms"; - Duration d1 = DurationUtils.ConvertDurationFromDaprFormat(s); - - String t = DurationUtils.ConvertDurationToDaprFormat(d1); - Assert.assertEquals(s, t); - } - - @Test - public void negativeDuration() { - Duration d = Duration.ofSeconds(-99); - String t = DurationUtils.ConvertDurationToDaprFormat(d); - Assert.assertEquals("", t); - } - - @Test - public void testGetHoursPart() { - Duration d1 = Duration.ZERO.plusHours(26); - Assert.assertEquals(2, DurationUtils.getHoursPart(d1)); - - Duration d2 = Duration.ZERO.plusHours(23); - Assert.assertEquals(23, DurationUtils.getHoursPart(d2)); - - Duration d3 = Duration.ZERO.plusHours(24); - Assert.assertEquals(0, DurationUtils.getHoursPart(d3)); - } - - @Test - public void testGetMinutesPart() { - Duration d1 = Duration.ZERO.plusMinutes(61); - Assert.assertEquals(1, DurationUtils.getMinutesPart(d1)); - - Duration d2 = Duration.ZERO.plusMinutes(60); - Assert.assertEquals(0, DurationUtils.getMinutesPart(d2)); - - Duration d3 = Duration.ZERO.plusMinutes(59); - Assert.assertEquals(59, DurationUtils.getMinutesPart(d3)); - - Duration d4 = Duration.ZERO.plusMinutes(3600); - Assert.assertEquals(0, DurationUtils.getMinutesPart(d4)); - } - - @Test - public void testGetSecondsPart() { - Duration d1 = Duration.ZERO.plusSeconds(61); - Assert.assertEquals(1, DurationUtils.getSecondsPart(d1)); - - Duration d2 = Duration.ZERO.plusSeconds(60); - Assert.assertEquals(0, DurationUtils.getSecondsPart(d2)); - - Duration d3 = Duration.ZERO.plusSeconds(59); - Assert.assertEquals(59, DurationUtils.getSecondsPart(d3)); - - Duration d4 = Duration.ZERO.plusSeconds(3600); - Assert.assertEquals(0, DurationUtils.getSecondsPart(d4)); - } - - @Test - public void testGetMillisecondsPart() { - Duration d1 = Duration.ZERO.plusMillis(61); - Assert.assertEquals(61, DurationUtils.getMilliSecondsPart(d1)); - - Duration d2 = Duration.ZERO.plusMillis(60); - Assert.assertEquals(60, DurationUtils.getMilliSecondsPart(d2)); - - Duration d3 = Duration.ZERO.plusMillis(59); - Assert.assertEquals(59, DurationUtils.getMilliSecondsPart(d3)); - - Duration d4 = Duration.ZERO.plusMillis(999); - Assert.assertEquals(999, DurationUtils.getMilliSecondsPart(d4)); - - Duration d5 = Duration.ZERO.plusMillis(1001); - Assert.assertEquals(1, DurationUtils.getMilliSecondsPart(d5)); - - Duration d6 = Duration.ZERO.plusMillis(1000); - Assert.assertEquals(0, DurationUtils.getMilliSecondsPart(d6)); - - Duration d7 = Duration.ZERO.plusMillis(10000); - Assert.assertEquals(0, DurationUtils.getMilliSecondsPart(d7)); - } -} diff --git a/sdk/src/main/java/io/dapr/client/DaprClient.java b/sdk/src/main/java/io/dapr/client/DaprClient.java index db3dd1fb35..2ab002af82 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClient.java +++ b/sdk/src/main/java/io/dapr/client/DaprClient.java @@ -118,13 +118,12 @@ public interface DaprClient { * Retrieve a State based on their key. * * @param state The key of the State to be retrieved. - * @param stateOptions + * @param stateOptions The options for the call to use. * @param clazz the Type of State needed as return. * @param the Type of the return. - * @param The Type of the key of the State. * @return A Mono Plan for the requested State. */ - Mono getState(StateKeyValue state, StateOptions stateOptions, Class clazz); + Mono> getState(StateKeyValue state, StateOptions stateOptions, Class clazz); /** * Save/Update a list of states. diff --git a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java index 1390870bf2..aa68854138 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientGrpcAdapter.java @@ -166,19 +166,24 @@ public Mono invokeBinding(String name, T request) { } /** + * @return Returns an io.dapr.client.domain.StateKeyValue + * * {@inheritDoc} */ @Override - public Mono getState(StateKeyValue key, StateOptions stateOptions, Class clazz) { + public Mono> getState(StateKeyValue state, StateOptions stateOptions, Class clazz) { try { DaprProtos.GetStateEnvelope.Builder builder = DaprProtos.GetStateEnvelope.newBuilder() - .setKey(key.getKey()) - .setConsistency(stateOptions.getConsistency().getValue()); + .setKey(state.getKey()); + if (stateOptions != null && stateOptions.getConsistency() != null) { + builder.setConsistency(stateOptions.getConsistency().getValue()); + } + DaprProtos.GetStateEnvelope envelope = builder.build(); ListenableFuture futureResponse = client.getState(envelope); return Mono.just(futureResponse).flatMap(f -> { try { - return Mono.just(objectSerializer.deserialize(f.get().getData().getValue().toStringUtf8(), clazz)); + return Mono.just(buildStateKeyValue(f.get(), state.getKey(), clazz)); } catch (Exception ex) { return Mono.error(ex); } @@ -188,6 +193,14 @@ public Mono getState(StateKeyValue key, StateOptions stateOptions, } } + private StateKeyValue buildStateKeyValue(DaprProtos.GetStateResponseEnvelope resonse, String requestedKey, Class clazz) throws IOException { + T value = objectSerializer.deserialize(resonse.getData().getValue().toByteArray(), clazz); + String etag = resonse.getEtag(); + String key = requestedKey; + + return new StateKeyValue<>(value, key, etag); + } + /** * {@inheritDoc} */ @@ -390,22 +403,24 @@ public Mono unregisterActorTimer(String actorType, String actorId, String /** * Builds the object io.dapr.{@link DaprProtos.InvokeServiceEnvelope} to be send based on the parameters. - * @param verb - * @param appId - * @param method - * @param request - * @param - * @return - * @throws IOException + * @param verb String that must match HTTP Methods + * @param appId The application id to be invoked + * @param method The application method to be invoked + * @param request The body of the request to be send as part of the invokation + * @param The Type of the Body + * @return The object to be sent as part of the invokation. + * @throws IOException If there's an issue serializing the request. */ private DaprProtos.InvokeServiceEnvelope buildInvokeServiceEnvelope( String verb, String appId, String method, K request) throws IOException { - byte[] byteRequest = objectSerializer.serialize(request); - Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteRequest)).build(); DaprProtos.InvokeServiceEnvelope.Builder envelopeBuilder = DaprProtos.InvokeServiceEnvelope.newBuilder() .setId(appId) - .setMethod(verb) - .setData(data); + .setMethod(verb); + if (request != null) { + byte[] byteRequest = objectSerializer.serialize(request); + Any data = Any.newBuilder().setValue(ByteString.copyFrom(byteRequest)).build(); + envelopeBuilder.setData(data); + } return envelopeBuilder.build(); } diff --git a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java index c51550c561..5bad41c59e 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java @@ -11,6 +11,7 @@ import io.dapr.utils.ObjectSerializer; import reactor.core.publisher.Mono; +import java.io.IOException; import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; @@ -169,7 +170,7 @@ public Mono invokeBinding(String name, T request) { * {@inheritDoc} */ @Override - public Mono getState(StateKeyValue state, StateOptions options, Class clazz) { + public Mono> getState(StateKeyValue state, StateOptions stateOptions, Class clazz) { try { if (state.getKey() == null) { throw new IllegalArgumentException("Name cannot be null or empty."); @@ -182,12 +183,12 @@ public Mono getState(StateKeyValue state, StateOptions options, Cla StringBuilder url = new StringBuilder(Constants.STATE_PATH) .append("/") .append(state.getKey()) - .append(getOptionsAsQueryParameter(options)); + .append(getOptionsAsQueryParameter(stateOptions)); return this.client .invokeAPI(DaprHttp.HttpMethods.GET.name(), url.toString(), headers) .flatMap(s -> { try { - return Mono.just(objectSerializer.deserialize(s, clazz)); + return Mono.just(buildStateKeyValue(s, state.getKey(), clazz)); } catch (Exception ex) { return Mono.error(ex); } @@ -262,7 +263,7 @@ public Mono invokeActorMethod(String actorType, String actorId, String m Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, jsonPayload, null); return Mono.just(responseMono).flatMap(f -> { try { - return Mono.just(f.block().getBody()); + return Mono.just(objectSerializer.deserialize(f.block().getBody(), String.class)); } catch (Exception ex) { return Mono.error(ex); } @@ -278,7 +279,7 @@ public Mono getActorState(String actorType, String actorId, String keyNa Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, "", null); return Mono.just(responseMono).flatMap(f -> { try { - return Mono.just(f.block().getBody()); + return Mono.just(objectSerializer.deserialize(f.block().getBody(), String.class)); } catch (Exception ex) { return Mono.error(ex); } @@ -376,4 +377,23 @@ private Map transformStateOptionsToMap(StateOptions options) return mapOptions; } + /** + * Builds a StateKeyValue object based on the Response + * @param resonse The response of the HTTP Call + * @param requestedKey The Key Requested. + * @param clazz The Class of the Value of the state + * @param The Type of the Value of the state + * @return A StateKeyValue instance + * @throws IOException If there's a issue deserialzing the response. + */ + private StateKeyValue buildStateKeyValue(DaprHttp.Response resonse, String requestedKey, Class clazz) throws IOException { + T value = objectSerializer.deserialize(resonse.getBody(), clazz); + String key = requestedKey; + String etag = null; + if (resonse.getHeaders() != null && resonse.getHeaders().containsKey("ETag")) { + etag = objectSerializer.deserialize(resonse.getHeaders().get("ETag"), String.class); + } + return new StateKeyValue<>(value, key, etag); + } + } diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index f5b617c981..e3527708d7 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -32,17 +32,17 @@ class DaprHttp { enum HttpMethods { GET, PUT, POST, DELETE; } static class Response { - private String body; + private byte[] body; private Map headers; private int statusCode; - public Response(String body, Map headers, int statusCode) { + public Response(byte[] body, Map headers, int statusCode) { this.body = body; this.headers = headers; this.statusCode = statusCode; } - public String getBody() { + public byte[] getBody() { return body; } @@ -182,7 +182,7 @@ public Mono invokeAPI(String method, String urlString, byte[] content, } Map mapHeaders = new HashMap<>(); - String result = response.body().string(); + byte[] result = response.body().bytes(); response.headers().forEach(pair -> { mapHeaders.put(pair.getFirst(), pair.getSecond()); }); diff --git a/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java b/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java index 97b99a8a1b..1f1e59c6a3 100644 --- a/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java +++ b/sdk/src/main/java/io/dapr/client/domain/StateKeyValue.java @@ -19,7 +19,7 @@ public class StateKeyValue { private final String key; /** * The ETag to be used - * For REDIS ONLY this must be an integer + * Keep in mind that for some state stores (like reids) only numbers are supported. */ private final String etag; @@ -58,4 +58,35 @@ public String getKey() { public String getEtag() { return etag; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof StateKeyValue)) return false; + + StateKeyValue that = (StateKeyValue) o; + + if (getValue() != null ? !getValue().equals(that.getValue()) : that.getValue() != null) return false; + if (getKey() != null ? !getKey().equals(that.getKey()) : that.getKey() != null) return false; + if (getEtag() != null ? !getEtag().equals(that.getEtag()) : that.getEtag() != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = getValue() != null ? getValue().hashCode() : 0; + result = 31 * result + (getKey() != null ? getKey().hashCode() : 0); + result = 31 * result + (getEtag() != null ? getEtag().hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "StateKeyValue{" + + "value=" + value + + ", key='" + key + '\'' + + ", etag='" + etag + '\'' + + '}'; + } } diff --git a/sdk/src/main/java/io/dapr/client/domain/StateOptions.java b/sdk/src/main/java/io/dapr/client/domain/StateOptions.java index 0772916dd6..414c84199a 100644 --- a/sdk/src/main/java/io/dapr/client/domain/StateOptions.java +++ b/sdk/src/main/java/io/dapr/client/domain/StateOptions.java @@ -7,9 +7,9 @@ import java.time.Duration; public class StateOptions { - private Consistency consistency; - private Concurrency concurrency; - private RetryPolicy retryPolicy; + private final Consistency consistency; + private final Concurrency concurrency; + private final RetryPolicy retryPolicy; public StateOptions(Consistency consistency, Concurrency concurrency, RetryPolicy retryPolicy) { this.consistency = consistency; @@ -33,7 +33,7 @@ public static enum Consistency { EVENTUAL("eventual"), STRONG("strong"); - private String value; + private final String value; private Consistency(String value) { this.value = value; @@ -48,7 +48,7 @@ public static enum Concurrency { FIRST_WRITE("first-write"), LAST_WRITE ("last-write"); - private String value; + private final String value; private Concurrency(String value) { this.value = value; @@ -75,9 +75,9 @@ public String getValue() { } } - private Duration interval; - private String threshold; - private Pattern pattern; + private final Duration interval; + private final String threshold; + private final Pattern pattern; public RetryPolicy(Duration interval, String threshold, Pattern pattern) { diff --git a/sdk/src/main/java/io/dapr/utils/DurationUtils.java b/sdk/src/main/java/io/dapr/utils/DurationUtils.java index ddfffe2ae2..1be79fa3a3 100644 --- a/sdk/src/main/java/io/dapr/utils/DurationUtils.java +++ b/sdk/src/main/java/io/dapr/utils/DurationUtils.java @@ -9,134 +9,134 @@ public class DurationUtils { - /** - * Converts time from the String format used by Dapr into a Duration. - * - * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). - * @return A Duration - */ - public static Duration ConvertDurationFromDaprFormat(String valueString) { - // Convert the format returned by the Dapr runtime into Duration - // An example of the format is: 4h15m50s60ms. It does not include days. - int hIndex = valueString.indexOf('h'); - int mIndex = valueString.indexOf('m'); - int sIndex = valueString.indexOf('s'); - int msIndex = valueString.indexOf("ms"); - - String hoursSpan = valueString.substring(0, hIndex); - - int hours = Integer.parseInt(hoursSpan); - int days = hours / 24; - hours = hours % 24; - - String minutesSpan = valueString.substring(hIndex + 1, mIndex); - int minutes = Integer.parseInt(minutesSpan); - - String secondsSpan = valueString.substring(mIndex + 1, sIndex); - int seconds = Integer.parseInt(secondsSpan); - - String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); - int milliseconds = Integer.parseInt(millisecondsSpan); - - return Duration.ZERO - .plusDays(days) - .plusHours(hours) - .plusMinutes(minutes) - .plusSeconds(seconds) - .plusMillis(milliseconds); + /** + * Converts time from the String format used by Dapr into a Duration. + * + * @param valueString A String representing time in the Dapr runtime's format (e.g. 4h15m50s60ms). + * @return A Duration + */ + public static Duration ConvertDurationFromDaprFormat(String valueString) { + // Convert the format returned by the Dapr runtime into Duration + // An example of the format is: 4h15m50s60ms. It does not include days. + int hIndex = valueString.indexOf('h'); + int mIndex = valueString.indexOf('m'); + int sIndex = valueString.indexOf('s'); + int msIndex = valueString.indexOf("ms"); + + String hoursSpan = valueString.substring(0, hIndex); + + int hours = Integer.parseInt(hoursSpan); + int days = hours / 24; + hours = hours % 24; + + String minutesSpan = valueString.substring(hIndex + 1, mIndex); + int minutes = Integer.parseInt(minutesSpan); + + String secondsSpan = valueString.substring(mIndex + 1, sIndex); + int seconds = Integer.parseInt(secondsSpan); + + String millisecondsSpan = valueString.substring(sIndex + 1, msIndex); + int milliseconds = Integer.parseInt(millisecondsSpan); + + return Duration.ZERO + .plusDays(days) + .plusHours(hours) + .plusMinutes(minutes) + .plusSeconds(seconds) + .plusMillis(milliseconds); + } + + /** + * Converts a Duration to the format used by the Dapr runtime. + * + * @param value Duration + * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) + */ + public static String ConvertDurationToDaprFormat(Duration value) { + String stringValue = ""; + + // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A + // negative "period" means fire once only. + if (value == Duration.ZERO || + (value.compareTo(Duration.ZERO) == 1)) { + long hours = getDaysPart(value) * 24 + getHoursPart(value); + + StringBuilder sb = new StringBuilder(); + + sb.append(hours); + sb.append("h"); + + sb.append(getMinutesPart((value))); + sb.append("m"); + + sb.append(getSecondsPart((value))); + sb.append("s"); + + sb.append(getMilliSecondsPart((value))); + sb.append("ms"); + + return sb.toString(); } - /** - * Converts a Duration to the format used by the Dapr runtime. - * - * @param value Duration - * @return The Duration formatted as a String in the format the Dapr runtime uses (e.g. 4h15m50s60ms) - */ - public static String ConvertDurationToDaprFormat(Duration value) { - String stringValue = ""; - - // return empty string for anything negative, it'll only happen for reminder "periods", not dueTimes. A - // negative "period" means fire once only. - if (value == Duration.ZERO || - (value.compareTo(Duration.ZERO) == 1)) { - long hours = getDaysPart(value) * 24 + getHoursPart(value); - - StringBuilder sb = new StringBuilder(); - - sb.append(hours); - sb.append("h"); - - sb.append(getMinutesPart((value))); - sb.append("m"); - - sb.append(getSecondsPart((value))); - sb.append("s"); - - sb.append(getMilliSecondsPart((value))); - sb.append("ms"); - - return sb.toString(); - } - - return stringValue; - } - - /** - * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. - * - * @param d Duration - * @return Number of days. - */ - static long getDaysPart(Duration d) { - long t = d.getSeconds() / 60 / 60 / 24; - return t; - } - - /** - * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. - * - * @param d The duration to parse - * @return the hour part of the duration - */ - static long getHoursPart(Duration d) { - long u = (d.getSeconds() / 60 / 60) % 24; - - return u; - } - - /** - * Helper to get the "minutes" part of the Duration. - * - * @param d The duration to parse - * @return the minutes part of the duration - */ - static long getMinutesPart(Duration d) { - long u = (d.getSeconds() / 60) % 60; - - return u; - } - - /** - * Helper to get the "seconds" part of the Duration. - * - * @param d The duration to parse - * @return the seconds part of the duration - */ - static long getSecondsPart(Duration d) { - long u = d.getSeconds() % 60; - - return u; - } - - /** - * Helper to get the "millis" part of the Duration. - * - * @param d The duration to parse - * @return the milliseconds part of the duration - */ - static long getMilliSecondsPart(Duration d) { - long u = d.toMillis() % 1000; - - return u; - } + return stringValue; + } + + /** + * Helper to get the "days" part of the Duration. For example if the duration is 26 hours, this returns 1. + * + * @param d Duration + * @return Number of days. + */ + static long getDaysPart(Duration d) { + long t = d.getSeconds() / 60 / 60 / 24; + return t; + } + + /** + * Helper to get the "hours" part of the Duration. For example if the duration is 26 hours, this is 1 day, 2 hours, so this returns 2. + * + * @param d The duration to parse + * @return the hour part of the duration + */ + static long getHoursPart(Duration d) { + long u = (d.getSeconds() / 60 / 60) % 24; + + return u; + } + + /** + * Helper to get the "minutes" part of the Duration. + * + * @param d The duration to parse + * @return the minutes part of the duration + */ + static long getMinutesPart(Duration d) { + long u = (d.getSeconds() / 60) % 60; + + return u; + } + + /** + * Helper to get the "seconds" part of the Duration. + * + * @param d The duration to parse + * @return the seconds part of the duration + */ + static long getSecondsPart(Duration d) { + long u = d.getSeconds() % 60; + + return u; + } + + /** + * Helper to get the "millis" part of the Duration. + * + * @param d The duration to parse + * @return the milliseconds part of the duration + */ + static long getMilliSecondsPart(Duration d) { + long u = d.toMillis() % 1000; + + return u; + } } diff --git a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java index cae3e4a1dd..b4a2f6821a 100644 --- a/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprClientGrpcAdapterTest.java @@ -7,6 +7,8 @@ import com.google.protobuf.Empty; import io.dapr.DaprGrpc; import io.dapr.DaprProtos; +import io.dapr.client.domain.StateKeyValue; +import io.dapr.client.domain.StateOptions; import io.dapr.client.domain.Verb; import io.dapr.utils.ObjectSerializer; import org.checkerframework.checker.nullness.compatqual.NullableDecl; @@ -17,6 +19,7 @@ import javax.annotation.Nullable; import java.io.IOException; +import java.time.Duration; import static com.google.common.util.concurrent.Futures.addCallback; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; @@ -93,10 +96,10 @@ public void publishEventCallbackExceptionThrownTest() { RuntimeException ex = new RuntimeException("An Exception"); MockCallback callback = new MockCallback(ex); addCallback(settableFuture, callback, directExecutor()); - settableFuture.setException(ex); when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); Mono result = adater.publishEvent("topic", "object"); + settableFuture.setException(ex); result.block(); } @@ -105,11 +108,12 @@ public void publishEventTest() { SettableFuture settableFuture = SettableFuture.create(); MockCallback callback = new MockCallback(Empty.newBuilder().build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(Empty.newBuilder().build()); when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); Mono result = adater.publishEvent("topic", "object"); + settableFuture.set(Empty.newBuilder().build()); result.block(); + assertTrue(callback.wasCalled); } @Test @@ -117,12 +121,62 @@ public void publishEventObjectTest() { SettableFuture settableFuture = SettableFuture.create(); MockCallback callback = new MockCallback(Empty.newBuilder().build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(Empty.newBuilder().build()); when(client.publishEvent(any(DaprProtos.PublishEventEnvelope.class))) .thenReturn(settableFuture); MyObject event = new MyObject(1, "Event"); Mono result = adater.publishEvent("topic", event); + settableFuture.set(Empty.newBuilder().build()); result.block(); + assertTrue(callback.wasCalled); + } + + @Test(expected = RuntimeException.class) + public void invokeBindingExceptionThrownTest() { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.invokeBinding("BindingName", "request"); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeBindingCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.setException(ex); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeBinding("BindingName", "request"); + result.block(); + } + + @Test + public void invokeBindingTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeBinding("BindingName", "request"); + settableFuture.set(Empty.newBuilder().build()); + result.block(); + assertTrue(callback.wasCalled); + } + + @Test + public void invokeBindingObjectTest() { + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback(Empty.newBuilder().build()); + addCallback(settableFuture, callback, directExecutor()); + when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + .thenReturn(settableFuture); + MyObject event = new MyObject(1, "Event"); + Mono result = adater.invokeBinding("BindingName", event); + settableFuture.set(Empty.newBuilder().build()); + result.block(); + assertTrue(callback.wasCalled); } @Test(expected = RuntimeException.class) @@ -155,11 +209,12 @@ public void invokeServiceVoidTest() throws Exception { new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() .setData(getAny("Value")).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); result.block(); + assertTrue(callback.wasCalled); } @Test @@ -170,12 +225,13 @@ public void invokeServiceVoidObjectTest() throws Exception { new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() .setData(getAny("Value")).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); MyObject request = new MyObject(1, "Event"); Mono result = adater.invokeService(Verb.GET, "appId", "method", request, null); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny("Value")).build()); result.block(); + assertTrue(callback.wasCalled); } @Test(expected = RuntimeException.class) @@ -193,10 +249,10 @@ public void invokeServiceCallbackExceptionThrownTest() { MockCallback callback = new MockCallback(ex); addCallback(settableFuture, callback, directExecutor()); - settableFuture.setException(ex); when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); Mono result = adater.invokeService(Verb.GET, "appId", "method", "request", null, String.class); + settableFuture.setException(ex); result.block(); } @@ -204,7 +260,6 @@ public void invokeServiceCallbackExceptionThrownTest() { public void invokeServiceTest() throws Exception { String expected = "Value"; SettableFuture settableFuture = SettableFuture.create(); - MockCallback callback = new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() .setData(getAny(expected)).build()); @@ -221,7 +276,6 @@ public void invokeServiceTest() throws Exception { public void invokeServiceObjectTest() throws Exception { MyObject resultObj = new MyObject(1, "Value"); SettableFuture settableFuture = SettableFuture.create(); - MockCallback callback = new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() .setData(getAny(resultObj)).build()); @@ -235,52 +289,241 @@ public void invokeServiceObjectTest() throws Exception { } @Test(expected = RuntimeException.class) - public void invokeBindingExceptionThrownTest() { + public void invokeServiceNoRequestBodyExceptionThrownTest() { when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenThrow(RuntimeException.class); - Mono result = adater.invokeBinding("BindingName", "request"); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); result.block(); } @Test(expected = RuntimeException.class) - public void invokeBindingCallbackExceptionThrownTest() { - SettableFuture settableFuture = SettableFuture.create(); + public void invokeServiceNoRequestCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); RuntimeException ex = new RuntimeException("An Exception"); - MockCallback callback = - new MockCallback(ex); + MockCallback callback = + new MockCallback(ex); addCallback(settableFuture, callback, directExecutor()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null, String.class); settableFuture.setException(ex); - when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + result.block(); + } + + @Test + public void invokeServiceNoRequestBodyTest() throws Exception { + String expected = "Value"; + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(expected)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeBinding("BindingName", "request"); + Mono result = adater.invokeService(Verb.GET, "appId", "method",null, String.class); + String strOutput = result.block(); + assertEquals(expected, strOutput); + } + + @Test + public void invokeServiceNoRequestBodyObjectTest() throws Exception { + MyObject resultObj = new MyObject(1, "Value"); + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(resultObj)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method",null, String.class); + String strOutput = result.block(); + assertEquals(serializer.serializeString(resultObj), strOutput); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceByteRequestExceptionThrownTest() throws IOException { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + String request = "Request"; + byte[] byteRequest = serializer.serialize(request); + Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceByteRequestCallbackExceptionThrownTest() throws IOException { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + String request = "Request"; + byte[] byteRequest = serializer.serialize(request); + Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + settableFuture.setException(ex); result.block(); } @Test - public void invokeBindingTest() { - SettableFuture settableFuture = SettableFuture.create(); - MockCallback callback = new MockCallback(Empty.newBuilder().build()); + public void invokeByteRequestServiceTest() throws Exception { + String expected = "Value"; + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(expected)).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(Empty.newBuilder().build()); - when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - Mono result = adater.invokeBinding("BindingName", "request"); + String request = "Request"; + byte[] byteRequest = serializer.serialize(request); + Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + byte[] byteOutput = result.block(); + String strOutput = serializer.deserialize(byteOutput, String.class); + assertEquals(expected, strOutput); + } + + @Test + public void invokeServiceByteRequestObjectTest() throws Exception { + MyObject resultObj = new MyObject(1, "Value"); + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(resultObj)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + String request = "Request"; + byte[] byteRequest = serializer.serialize(request); + Mono result = adater.invokeService(Verb.GET, "appId", "method", byteRequest, null); + byte[] byteOutput = result.block(); + assertEquals(resultObj, serializer.deserialize(byteOutput, MyObject.class)); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceNoRequestNoClassBodyExceptionThrownTest() { + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenThrow(RuntimeException.class); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void invokeServiceNoRequestNoClassCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback(ex); + addCallback(settableFuture, callback, directExecutor()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + settableFuture.setException(ex); result.block(); } @Test - public void invokeBindingObjectTest() { - SettableFuture settableFuture = SettableFuture.create(); - MockCallback callback = new MockCallback(Empty.newBuilder().build()); + public void invokeServiceNoRequestNoClassBodyTest() throws Exception { + String expected = "Value"; + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(expected)).build()); addCallback(settableFuture, callback, directExecutor()); - settableFuture.set(Empty.newBuilder().build()); - when(client.invokeBinding(any(DaprProtos.InvokeBindingEnvelope.class))) + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) .thenReturn(settableFuture); - MyObject event = new MyObject(1, "Event"); - Mono result = adater.invokeBinding("BindingName", event); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(expected)).build()); + result.block(); + assertTrue(callback.wasCalled); + } + + @Test + public void invokeServiceNoRequestNoClassBodyObjectTest() throws Exception { + MyObject resultObj = new MyObject(1, "Value"); + SettableFuture settableFuture = SettableFuture.create(); + + MockCallback callback = + new MockCallback(DaprProtos.InvokeServiceResponseEnvelope.newBuilder() + .setData(getAny(resultObj)).build()); + addCallback(settableFuture, callback, directExecutor()); + settableFuture.set(DaprProtos.InvokeServiceResponseEnvelope.newBuilder().setData(getAny(resultObj)).build()); + when(client.invokeService(any(DaprProtos.InvokeServiceEnvelope.class))) + .thenReturn(settableFuture); + Mono result = adater.invokeService(Verb.GET, "appId", "method", null); + result.block(); + assertTrue(callback.wasCalled); + } + + @Test(expected = RuntimeException.class) + public void getStateExceptionThrownTest() { + when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))).thenThrow(RuntimeException.class); + StateKeyValue key = buildStateKey(null, "Key1", "ETag1"); + Mono> result = adater.getState(key, null, String.class); + result.block(); + } + + @Test(expected = RuntimeException.class) + public void getStateCallbackExceptionThrownTest() { + SettableFuture settableFuture = SettableFuture.create(); + RuntimeException ex = new RuntimeException("An Exception"); + MockCallback callback = + new MockCallback<>(ex); + addCallback(settableFuture, callback, directExecutor()); + when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) + .thenReturn(settableFuture); + StateKeyValue key = buildStateKey(null, "Key1", "ETag1"); + Mono> result = adater.getState(key, null, String.class); + settableFuture.setException(ex); result.block(); } + @Test + public void getStateStringValueNoOptionsTest() throws IOException { + String etag = "ETag1"; + String key = "key1"; + String expectedValue = "Expected state"; + StateKeyValue expectedState = buildStateKey(expectedValue, key, etag); + DaprProtos.GetStateResponseEnvelope responseEnvelope = DaprProtos.GetStateResponseEnvelope.newBuilder() + .setData(getAny(expectedValue)) + .setEtag(etag) + .build(); + SettableFuture settableFuture = SettableFuture.create(); + MockCallback callback = new MockCallback<>(responseEnvelope); + addCallback(settableFuture, callback, directExecutor()); + when(client.getState(any(io.dapr.DaprProtos.GetStateEnvelope.class))) + .thenReturn(settableFuture); + StateKeyValue keyRequest = buildStateKey(null, key, etag); + Mono> result = adater.getState(keyRequest, null, String.class); + settableFuture.set(responseEnvelope); + assertEquals(expectedState, result.block()); + } + + private StateKeyValue buildStateKey(T value, String key, String etag) { + return new StateKeyValue(value, key, etag); + } + + private StateOptions buildStateOptions(StateOptions.Consistency consistency, StateOptions.Concurrency concurrency, + Duration interval, String threshold, StateOptions.RetryPolicy.Pattern pattern) { + + StateOptions.RetryPolicy retryPolicy = null; + if (interval != null || threshold != null || pattern != null) { + retryPolicy = new StateOptions.RetryPolicy(interval, threshold, pattern); + } + StateOptions options = null; + if (consistency != null || concurrency != null || retryPolicy != null) { + options = new StateOptions(consistency, concurrency, retryPolicy); + } + return options; + } + private Any getAny(T value) throws IOException { byte[] byteValue = serializer.serialize(value); return Any.newBuilder().setValue(ByteString.copyFrom(byteValue)).build(); @@ -343,5 +586,25 @@ public String getValue() { public void setValue(String value) { this.value = value; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof MyObject)) return false; + + MyObject myObject = (MyObject) o; + + if (!getId().equals(myObject.getId())) return false; + if (getValue() != null ? !getValue().equals(myObject.getValue()) : myObject.getValue() != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = getId().hashCode(); + result = 31 * result + (getValue() != null ? getValue().hashCode() : 0); + return result; + } } } diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpStub.java b/sdk/src/test/java/io/dapr/client/DaprHttpStub.java index 7c3a2eb7ca..6acad1b193 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpStub.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpStub.java @@ -16,7 +16,7 @@ public class DaprHttpStub extends DaprHttp { public static class ResponseStub extends DaprHttp.Response { - public ResponseStub(String body, Map headers, int statusCode) { + public ResponseStub(byte[] body, Map headers, int statusCode) { super(body, headers, statusCode); } } diff --git a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java index 3baa82409d..99d41aec69 100644 --- a/sdk/src/test/java/io/dapr/client/DaprHttpTest.java +++ b/sdk/src/test/java/io/dapr/client/DaprHttpTest.java @@ -4,6 +4,7 @@ */ package io.dapr.client; +import io.dapr.utils.ObjectSerializer; import okhttp3.*; import okhttp3.mock.Behavior; import okhttp3.mock.MockInterceptor; @@ -24,6 +25,8 @@ public class DaprHttpTest { private MockInterceptor mockInterceptor; + private ObjectSerializer serializer = new ObjectSerializer(); + private final String EXPECTED_RESULT = "{\"data\":\"ewoJCSJwcm9wZXJ0eUEiOiAidmFsdWVBIiwKCQkicHJvcGVydHlCIjogInZhbHVlQiIKCX0=\"}"; @Before @@ -42,7 +45,9 @@ public void invokePostMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); Mono mono = daprHttp.invokeAPI("POST","v1.0/state",null); - assertEquals(EXPECTED_RESULT,mono.block().getBody()); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + assertEquals(EXPECTED_RESULT,body); } @@ -56,7 +61,9 @@ public void invokeDeleteMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); Mono mono = daprHttp.invokeAPI("DELETE","v1.0/state",null); - assertEquals(EXPECTED_RESULT,mono.block().getBody()); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + assertEquals(EXPECTED_RESULT,body); } @@ -70,13 +77,14 @@ public void invokeGetMethod() throws IOException { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); Mono mono = daprHttp.invokeAPI("GET","v1.0/get",null); - - assertEquals(EXPECTED_RESULT,mono.block().getBody()); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + assertEquals(EXPECTED_RESULT,body); } @Test - public void invokeMethodWithHeaders() { + public void invokeMethodWithHeaders() throws IOException { Map headers = new HashMap<>(); headers.put("header","value"); @@ -88,13 +96,14 @@ public void invokeMethodWithHeaders() { DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); - - assertEquals(EXPECTED_RESULT,mono.block().getBody()); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + assertEquals(EXPECTED_RESULT,body); } @Test(expected = RuntimeException.class) - public void invokeMethodRuntimeException(){ + public void invokeMethodRuntimeException() throws IOException { Map headers = new HashMap<>(); headers.put("header","value"); @@ -108,8 +117,9 @@ public void invokeMethodRuntimeException(){ DaprHttp daprHttp = new DaprHttp("http://localhost",3500,okHttpClient); Mono mono = daprHttp.invokeAPI("GET","v1.0/get",headers); - - assertEquals(EXPECTED_RESULT,mono.block().getBody()); + DaprHttp.Response response = mono.block(); + String body = serializer.deserialize(response.getBody(), String.class); + assertEquals(EXPECTED_RESULT,body); } } \ No newline at end of file diff --git a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java index 9146cc4e6b..c6c7f374d8 100644 --- a/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java +++ b/sdk/src/test/java/io/dapr/runtime/DaprRuntimeTest.java @@ -205,7 +205,7 @@ public void invokeHappyCase() throws Exception { METHOD_NAME, message.data, message.metadata) - .map(r -> new DaprHttpStub.ResponseStub(new String(r, StandardCharsets.UTF_8), null, 200))); + .map(r -> new DaprHttpStub.ResponseStub(r, null, 200))); Mono response = client.invokeService(Verb.POST, APP_ID, METHOD_NAME, message.data, message.metadata); Assert.assertEquals( From 18f0db31fd92d6998ad5afbe45d5ccac70917098 Mon Sep 17 00:00:00 2001 From: "andres.robles" Date: Mon, 13 Jan 2020 09:21:50 -0600 Subject: [PATCH 5/5] Addressing PR comments --- .../io/dapr/client/DaprClientHttpAdapter.java | 13 ++++++------- sdk/src/main/java/io/dapr/client/DaprHttp.java | 17 ++++++----------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java index 5bad41c59e..08cb3ad5d5 100644 --- a/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java +++ b/sdk/src/main/java/io/dapr/client/DaprClientHttpAdapter.java @@ -95,10 +95,9 @@ public Mono invokeService(Verb verb, String appId, String method, R re String path = String.format("%s/%s/method/%s", Constants.INVOKE_PATH, appId, method); byte[] serializedRequestBody = objectSerializer.serialize(request); Mono response = this.client.invokeAPI(httMethod, path, serializedRequestBody, metadata); - return Mono.just(response) - .flatMap(r -> { + return response.flatMap(r -> { try { - return Mono.just(objectSerializer.deserialize(r.block().getBody(), clazz)); + return Mono.just(objectSerializer.deserialize(r.getBody(), clazz)); } catch (Exception ex) { return Mono.error(ex); } @@ -261,9 +260,9 @@ public Mono deleteState(StateKeyValue state, StateOptions options) public Mono invokeActorMethod(String actorType, String actorId, String methodName, String jsonPayload) { String url = String.format(Constants.ACTOR_METHOD_RELATIVE_URL_FORMAT, actorType, actorId, methodName); Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.POST.name(), url, jsonPayload, null); - return Mono.just(responseMono).flatMap(f -> { + return responseMono.flatMap(f -> { try { - return Mono.just(objectSerializer.deserialize(f.block().getBody(), String.class)); + return Mono.just(objectSerializer.deserialize(f.getBody(), String.class)); } catch (Exception ex) { return Mono.error(ex); } @@ -277,9 +276,9 @@ public Mono invokeActorMethod(String actorType, String actorId, String m public Mono getActorState(String actorType, String actorId, String keyName) { String url = String.format(Constants.ACTOR_STATE_KEY_RELATIVE_URL_FORMAT, actorType, actorId, keyName); Mono responseMono = this.client.invokeAPI(DaprHttp.HttpMethods.GET.name(), url, "", null); - return Mono.just(responseMono).flatMap(f -> { + return responseMono.flatMap(f -> { try { - return Mono.just(objectSerializer.deserialize(f.block().getBody(), String.class)); + return Mono.just(objectSerializer.deserialize(f.getBody(), String.class)); } catch (Exception ex) { return Mono.error(ex); } diff --git a/sdk/src/main/java/io/dapr/client/DaprHttp.java b/sdk/src/main/java/io/dapr/client/DaprHttp.java index e3527708d7..41af03a5cf 100644 --- a/sdk/src/main/java/io/dapr/client/DaprHttp.java +++ b/sdk/src/main/java/io/dapr/client/DaprHttp.java @@ -29,7 +29,7 @@ class DaprHttp { /** * HTTP Methods supported. */ - enum HttpMethods { GET, PUT, POST, DELETE; } + enum HttpMethods {GET, PUT, POST, DELETE;} static class Response { private byte[] body; @@ -113,7 +113,7 @@ public int getStatusCode() { * @return Asynchronous text */ public Mono invokeAPI(String method, String urlString, Map headers) { - return this.invokeAPI(method, urlString, (byte[])null, headers); + return this.invokeAPI(method, urlString, (byte[]) null, headers); } /** @@ -149,7 +149,7 @@ public Mono invokeAPI(String method, String urlString, byte[] content, body = mediaType.equals(MEDIA_TYPE_APPLICATION_JSON) ? REQUEST_BODY_EMPTY_JSON : RequestBody.Companion.create(new byte[0], mediaType); } else { - body = RequestBody.Companion.create(content, mediaType); + body = RequestBody.Companion.create(content, mediaType); } Request.Builder requestBuilder = new Request.Builder() @@ -173,7 +173,7 @@ public Mono invokeAPI(String method, String urlString, byte[] content, try (okhttp3.Response response = this.httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { - DaprError error = parseDaprError(response.body().string()); + DaprError error = parseDaprError(response.body().bytes()); if ((error != null) && (error.getErrorCode() != null) && (error.getMessage() != null)) { throw new RuntimeException(new DaprException(error)); } @@ -200,16 +200,11 @@ public Mono invokeAPI(String method, String urlString, byte[] content, * @param json Response body from Dapr. * @return DaprError or null if could not parse. */ - private static DaprError parseDaprError(String json) { + private static DaprError parseDaprError(byte[] json) throws IOException { if (json == null) { return null; } - - try { - return OBJECT_MAPPER.readValue(json, DaprError.class); - } catch (IOException e) { - throw new DaprException("500", "Unknown error: could not parse error json."); - } + return OBJECT_MAPPER.readValue(json, DaprError.class); } }