mirror of https://github.com/dapr/dotnet-sdk.git
Tentative fix for timers deserializing error (#1512)
* Tentative fix for deserializing error * Added unit tests to prove out timer deserialization for all supported formats Signed-off-by: Whit Waldo <whit.waldo@innovian.net>
This commit is contained in:
parent
6f07643280
commit
32d06a7136
|
@ -223,7 +223,7 @@ namespace Dapr.Actors.Runtime
|
||||||
internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default)
|
internal async Task FireTimerAsync(ActorId actorId, Stream requestBodyStream, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
#pragma warning disable 0618
|
#pragma warning disable 0618
|
||||||
var timerData = await JsonSerializer.DeserializeAsync<TimerInfo>(requestBodyStream);
|
var timerData = await DeserializeAsync(requestBodyStream);
|
||||||
#pragma warning restore 0618
|
#pragma warning restore 0618
|
||||||
|
|
||||||
// Create a Func to be invoked by common method.
|
// Create a Func to be invoked by common method.
|
||||||
|
@ -243,6 +243,62 @@ namespace Dapr.Actors.Runtime
|
||||||
await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken);
|
await this.DispatchInternalAsync(actorId, this.timerMethodContext, RequestFunc, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable 0618
|
||||||
|
internal static async Task<TimerInfo> DeserializeAsync(Stream stream)
|
||||||
|
{
|
||||||
|
var json = await JsonSerializer.DeserializeAsync<JsonElement>(stream);
|
||||||
|
if (json.ValueKind == JsonValueKind.Null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var setAnyProperties = false; // Used to determine if anything was actually deserialized
|
||||||
|
var dueTime = TimeSpan.Zero;
|
||||||
|
var callback = "";
|
||||||
|
var period = TimeSpan.Zero;
|
||||||
|
var data = Array.Empty<byte>();
|
||||||
|
TimeSpan? ttl = null;
|
||||||
|
if (json.TryGetProperty("callback", out var callbackProperty))
|
||||||
|
{
|
||||||
|
setAnyProperties = true;
|
||||||
|
callback = callbackProperty.GetString();
|
||||||
|
}
|
||||||
|
if (json.TryGetProperty("dueTime", out var dueTimeProperty))
|
||||||
|
{
|
||||||
|
setAnyProperties = true;
|
||||||
|
var dueTimeString = dueTimeProperty.GetString();
|
||||||
|
dueTime = ConverterUtils.ConvertTimeSpanFromDaprFormat(dueTimeString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.TryGetProperty("period", out var periodProperty))
|
||||||
|
{
|
||||||
|
setAnyProperties = true;
|
||||||
|
var periodString = periodProperty.GetString();
|
||||||
|
(period, _) = ConverterUtils.ConvertTimeSpanValueFromISO8601Format(periodString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.TryGetProperty("data", out var dataProperty) && dataProperty.ValueKind != JsonValueKind.Null)
|
||||||
|
{
|
||||||
|
setAnyProperties = true;
|
||||||
|
data = dataProperty.GetBytesFromBase64();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.TryGetProperty("ttl", out var ttlProperty))
|
||||||
|
{
|
||||||
|
setAnyProperties = true;
|
||||||
|
var ttlString = ttlProperty.GetString();
|
||||||
|
ttl = ConverterUtils.ConvertTimeSpanFromDaprFormat(ttlString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!setAnyProperties)
|
||||||
|
{
|
||||||
|
return null; //No properties were ever deserialized, so return null instead of default values
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TimerInfo(callback, data, dueTime, period, ttl);
|
||||||
|
}
|
||||||
|
#pragma warning restore 0618
|
||||||
|
|
||||||
internal async Task ActivateActorAsync(ActorId actorId)
|
internal async Task ActivateActorAsync(ActorId actorId)
|
||||||
{
|
{
|
||||||
// An actor is activated by "Dapr" runtime when a call is to be made for an actor.
|
// An actor is activated by "Dapr" runtime when a call is to be made for an actor.
|
||||||
|
|
|
@ -103,7 +103,7 @@ internal static class ConverterUtils
|
||||||
builder.Append($"{value.Days}D");
|
builder.Append($"{value.Days}D");
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Append("T");
|
builder.Append('T');
|
||||||
|
|
||||||
if(value.Hours > 0)
|
if(value.Hours > 0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Dapr.Actors.Client;
|
using Dapr.Actors.Client;
|
||||||
|
@ -175,6 +177,160 @@ namespace Dapr.Actors.Runtime
|
||||||
Assert.Equal(1, activator.DeleteCallCount);
|
Assert.Equal(1, activator.DeleteCallCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_Iso8601_Time()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"0h0m7s10ms\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Every()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 15s\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(15), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Every2()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@every 3h2m15s\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromHours(3).Add(TimeSpan.FromMinutes(2)).Add(TimeSpan.FromSeconds(15)), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Monthly()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@monthly\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromDays(30), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Weekly()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@weekly\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromDays(7), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Daily()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@daily\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromDays(1), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Period_DaprFormat_Hourly()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"period\": \"@hourly\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.FromHours(1), result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_DueTime_DaprFormat_Hourly()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"@hourly\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.FromHours(1), result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_DueTime_Iso8601Times()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"dueTime\": \"0h0m7s10ms\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Null(result.Ttl);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.DueTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Ttl_DaprFormat_Hourly()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"@hourly\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||||
|
Assert.Equal(TimeSpan.FromHours(1), result.Ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DeserializeTimer_Ttl_Iso8601Times()
|
||||||
|
{
|
||||||
|
const string timerJson = "{\"callback\": \"TimerCallback\", \"ttl\": \"0h0m7s10ms\"}";
|
||||||
|
await using var stream = new MemoryStream(Encoding.UTF8.GetBytes(timerJson));
|
||||||
|
var result = await ActorManager.DeserializeAsync(stream);
|
||||||
|
|
||||||
|
Assert.Equal("TimerCallback", result.Callback);
|
||||||
|
Assert.Equal(Array.Empty<byte>(), result.Data);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.DueTime);
|
||||||
|
Assert.Equal(TimeSpan.Zero, result.Period);
|
||||||
|
Assert.Equal(TimeSpan.FromSeconds(7).Add(TimeSpan.FromMilliseconds(10)), result.Ttl);
|
||||||
|
}
|
||||||
|
|
||||||
private interface ITestActor : IActor { }
|
private interface ITestActor : IActor { }
|
||||||
|
|
||||||
private class TestActor : Actor, ITestActor, IDisposable
|
private class TestActor : Actor, ITestActor, IDisposable
|
||||||
|
|
Loading…
Reference in New Issue