-
Notifications
You must be signed in to change notification settings - Fork 335
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes: #444 ActorId doesn't define a default constructor, and so it's not deserializable via System.Text.Json. This change implements a converter so that ActorId's API shape can work with the serializer properly. I added an integration test for ActorReference as well, but no library changes we needed for that, ActorId was the blocker.
- Loading branch information
Showing
3 changed files
with
162 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// ------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
// ------------------------------------------------------------ | ||
|
||
namespace Dapr.Actors.Seralization | ||
{ | ||
using System; | ||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
|
||
// Converter for ActorId - will be serialized as a JSON string | ||
internal class ActorIdJsonConverter : JsonConverter<ActorId> | ||
{ | ||
public override ActorId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
if (typeToConvert != typeof(ActorId)) | ||
{ | ||
throw new ArgumentException( $"Conversion to the type '{typeToConvert}' is not supported.", nameof(typeToConvert)); | ||
} | ||
|
||
// Note - we generate random Guids for Actor Ids when we're generating them randomly | ||
// but we don't actually enforce a format. Ids could be a number, or a date, or whatever, | ||
// we don't really care. However we always **represent** Ids in JSON as strings. | ||
if (reader.TokenType == JsonTokenType.String && | ||
reader.GetString() is string text && | ||
!string.IsNullOrWhiteSpace(text)) | ||
{ | ||
return new ActorId(text); | ||
} | ||
else if (reader.TokenType == JsonTokenType.Null) | ||
{ | ||
return null; | ||
} | ||
|
||
throw new JsonException(); // The serializer will provide a default error message. | ||
} | ||
|
||
public override void Write(Utf8JsonWriter writer, ActorId value, JsonSerializerOptions options) | ||
{ | ||
if (value is null) | ||
{ | ||
writer.WriteNullValue(); | ||
} | ||
else | ||
{ | ||
writer.WriteStringValue(value.GetId()); | ||
} | ||
} | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
test/Dapr.Actors.Test/Serialization/ActorIdJsonConverterTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
// ------------------------------------------------------------ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
// ------------------------------------------------------------ | ||
|
||
using System.Text.Json; | ||
using System.Text.Json.Serialization; | ||
using Xunit; | ||
|
||
namespace Dapr.Actors.Serialization | ||
{ | ||
public class ActorIdJsonConverterTest | ||
{ | ||
[Fact] | ||
public void CanSerializeActorId() | ||
{ | ||
var id = ActorId.CreateRandom(); | ||
var document = new { actor = id, }; | ||
|
||
// We use strings for ActorId - the result should be the same as passing the Id directly. | ||
var expected = JsonSerializer.Serialize(new { actor = id.GetId(), }); | ||
|
||
var serialized = JsonSerializer.Serialize(document); | ||
|
||
Assert.Equal(expected, serialized); | ||
} | ||
|
||
[Fact] | ||
public void CanSerializeNullActorId() | ||
{ | ||
var document = new { actor = (ActorId)null, }; | ||
|
||
var expected = JsonSerializer.Serialize(new { actor = (string)null, }); | ||
|
||
var serialized = JsonSerializer.Serialize(document); | ||
|
||
Assert.Equal(expected, serialized); | ||
} | ||
|
||
[Fact] | ||
public void CanDeserializeActorId() | ||
{ | ||
var id = ActorId.CreateRandom().GetId(); | ||
var document = $@" | ||
{{ | ||
""actor"": ""{id}"" | ||
}}"; | ||
|
||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document); | ||
|
||
Assert.Equal(id, deserialized.Actor.GetId()); | ||
} | ||
|
||
[Fact] | ||
public void CanDeserializeNullActorId() | ||
{ | ||
var id = ActorId.CreateRandom().GetId(); | ||
var document = $@" | ||
{{ | ||
""actor"": null | ||
}}"; | ||
|
||
var deserialized = JsonSerializer.Deserialize<ActorHolder>(document); | ||
|
||
Assert.Null(deserialized.Actor); | ||
} | ||
|
||
[Theory] | ||
[InlineData("{ \"actor\": ")] | ||
[InlineData("{ \"actor\": \"hi")] | ||
[InlineData("{ \"actor\": }")] | ||
[InlineData("{ \"actor\": 3 }")] | ||
[InlineData("{ \"actor\": \"\"}")] | ||
[InlineData("{ \"actor\": \" \"}")] | ||
public void CanReportErrorsFromInvalidData(string document) | ||
{ | ||
// The error messages are provided by the serializer, don't test them here | ||
// that would be fragile. | ||
Assert.Throws<JsonException>(() => | ||
{ | ||
JsonSerializer.Deserialize<ActorHolder>(document); | ||
}); | ||
} | ||
|
||
// Regression test for #444 | ||
[Fact] | ||
public void CanRoundTripActorReference() | ||
{ | ||
var reference = new ActorReference() | ||
{ | ||
ActorId = ActorId.CreateRandom(), | ||
ActorType = "TestActor", | ||
}; | ||
|
||
var serialized = JsonSerializer.Serialize(reference); | ||
var deserialized = JsonSerializer.Deserialize<ActorReference>(serialized); | ||
|
||
Assert.Equal(reference.ActorId.GetId(), deserialized.ActorId.GetId()); | ||
Assert.Equal(reference.ActorType, deserialized.ActorType); | ||
} | ||
|
||
private class ActorHolder | ||
{ | ||
[JsonPropertyName("actor")] | ||
public ActorId Actor { get; set; } | ||
} | ||
} | ||
} |