Skip to content

Commit

Permalink
Support for @bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
adambollen committed Oct 17, 2024
1 parent 7aa1a29 commit c1b452e
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 6 deletions.
12 changes: 12 additions & 0 deletions Fauna.Test/Integration.Tests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Fauna.Exceptions;
using Fauna.Mapping;
using Fauna.Types;
Expand Down Expand Up @@ -566,4 +567,15 @@ public void ValidateUnwrappedMapOfQueriesError()
var ex = Assert.ThrowsAsync<SerializationException>(async () => await _client.QueryAsync<Dictionary<string, int>>(FQL($"{q}")));
Assert.IsTrue(ex!.Message.Contains("Unable to deserialize `FaunaType.Object` with `IntSerializer`"));
}

[Test]
[Category("serialization")]
public async Task ValidateBytesAcrossTheWire()
{
byte[] byteArray = { 70, 97, 117, 110, 97 };

var result = await _client.QueryAsync<byte[]>(FQL($"let x:Bytes = {byteArray}; x"));

Assert.AreEqual(Encoding.UTF8.GetBytes("Fauna"), result.Data);
}
}
17 changes: 17 additions & 0 deletions Fauna.Test/Serialization/Utf8FaunaReader.Tests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text;
using Fauna.Exceptions;
using Fauna.Serialization;
using Fauna.Types;
Expand Down Expand Up @@ -51,6 +52,9 @@ private static void AssertReader(Utf8FaunaReader reader, IEnumerable<Tuple<Token
case TokenType.Module:
Assert.AreEqual(obj, reader.GetModule());
break;
case TokenType.Bytes:
Assert.AreEqual(obj, reader.GetBytes());
break;
default:
Assert.Null(obj);
break;
Expand Down Expand Up @@ -122,6 +126,19 @@ public void ReadInt()
AssertReader(reader, expectedTokens);
}

[Test]
public void ReadBytes()
{
const string s = @"{""@bytes"": ""RmF1bmE=""}";
var reader = new Utf8FaunaReader(s);
var expectedTokens = new List<Tuple<TokenType, object?>>()
{
new(TokenType.Bytes, Encoding.UTF8.GetBytes("Fauna"))
};

AssertReader(reader, expectedTokens);
}

[Test]
public void ReadLong()
{
Expand Down
13 changes: 11 additions & 2 deletions Fauna.Test/Serialization/Utf8FaunaWriter.Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ public void WriteTime()
AssertWriter(@"{""@time"":""2023-01-01T14:04:30.0010010Z""}");
}

[Test]
public void WriteBytesValue()
{
_writer.WriteBytesValue(Encoding.UTF8.GetBytes("Fauna"));
AssertWriter(@"{""@bytes"":""RmF1bmE=""}");
}

[Test]
public void WriteObject()
{
Expand All @@ -110,6 +117,7 @@ public void WriteObject()
_writer.WriteDouble("aDecimal", 3.14M);
_writer.WriteBoolean("true", true);
_writer.WriteBoolean("false", false);
_writer.WriteBytes("someBytes", Encoding.UTF8.GetBytes("Fauna"));
_writer.WriteString("foo", "bar");
_writer.WriteDate("aDate", new DateTime(2023, 12, 4));
_writer.WriteTime("aTime", new DateTime(2023, 12, 4, 0, 0, 0, 0, DateTimeKind.Utc));
Expand All @@ -121,7 +129,7 @@ public void WriteObject()
_writer.WriteStartObject();
_writer.WriteEndObject();
_writer.WriteEndObject();
AssertWriter(@"{""anInt"":{""@int"":""42""},""aLong"":{""@long"":""42""},""aDouble"":{""@double"":""1.2""},""aDecimal"":{""@double"":""3.14""},""true"":true,""false"":false,""foo"":""bar"",""aDate"":{""@date"":""2023-12-04""},""aTime"":{""@time"":""2023-12-04T00:00:00.0000000Z""},""aNull"":null,""anArray"":[],""anObject"":{}}");
AssertWriter(@"{""anInt"":{""@int"":""42""},""aLong"":{""@long"":""42""},""aDouble"":{""@double"":""1.2""},""aDecimal"":{""@double"":""3.14""},""true"":true,""false"":false,""someBytes"":{""@bytes"":""RmF1bmE=""},""foo"":""bar"",""aDate"":{""@date"":""2023-12-04""},""aTime"":{""@time"":""2023-12-04T00:00:00.0000000Z""},""aNull"":null,""anArray"":[],""anObject"":{}}");
}

[Test]
Expand All @@ -134,6 +142,7 @@ public void WriteArray()
_writer.WriteDoubleValue(3.14M);
_writer.WriteBooleanValue(true);
_writer.WriteBooleanValue(false);
_writer.WriteBytesValue(Encoding.UTF8.GetBytes("Fauna"));
_writer.WriteStringValue("bar");
_writer.WriteDateValue(new DateTime(2023, 12, 4));
_writer.WriteTimeValue(new DateTime(2023, 12, 4, 0, 0, 0, 0, DateTimeKind.Utc));
Expand All @@ -143,7 +152,7 @@ public void WriteArray()
_writer.WriteStartObject();
_writer.WriteEndObject();
_writer.WriteEndArray();
AssertWriter(@"[{""@int"":""42""},{""@long"":""42""},{""@double"":""1.2""},{""@double"":""3.14""},true,false,""bar"",{""@date"":""2023-12-04""},{""@time"":""2023-12-04T00:00:00.0000000Z""},null,[],{}]");
AssertWriter(@"[{""@int"":""42""},{""@long"":""42""},{""@double"":""1.2""},{""@double"":""3.14""},true,false,{""@bytes"":""RmF1bmE=""},""bar"",{""@date"":""2023-12-04""},{""@time"":""2023-12-04T00:00:00.0000000Z""},null,[],{}]");
}

[Test]
Expand Down
4 changes: 3 additions & 1 deletion Fauna/Serialization/Serializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ public static class Serializer

internal static readonly HashSet<string> Tags = new()
{
"@int", "@long", "@double", "@date", "@time", "@mod", "@stream", "@ref", "@doc", "@set", "@object"
"@int", "@long", "@double", "@date", "@time", "@mod", "@stream", "@ref", "@doc", "@set", "@object", "@bytes"
};

private static readonly DynamicSerializer s_object = DynamicSerializer.Singleton;
private static readonly StringSerializer s_string = new();
private static readonly ByteSerializer s_byte = new();
private static readonly BytesSerializer s_bytes = new();
private static readonly SByteSerializer s_sbyte = new();
private static readonly ShortSerializer s_short = new();
private static readonly UShortSerializer s_ushort = new();
Expand Down Expand Up @@ -79,6 +80,7 @@ public static ISerializer Generate(MappingContext context, Type targetType)
if (targetType == typeof(object)) return s_object;
if (targetType == typeof(string)) return s_string;
if (targetType == typeof(byte)) return s_byte;
if (targetType == typeof(byte[])) return s_bytes;
if (targetType == typeof(sbyte)) return s_sbyte;
if (targetType == typeof(short)) return s_short;
if (targetType == typeof(ushort)) return s_ushort;
Expand Down
27 changes: 27 additions & 0 deletions Fauna/Serialization/StructSerializers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,33 @@ public override void Serialize(MappingContext context, Utf8FaunaWriter writer, o
public override List<FaunaType> GetSupportedTypes() => new List<FaunaType> { FaunaType.Int, FaunaType.Null };
}

internal class BytesSerializer : BaseSerializer<byte[]>
{
public override byte[] Deserialize(MappingContext context, ref Utf8FaunaReader reader) =>
reader.CurrentTokenType switch
{
TokenType.Bytes => reader.GetBytes(),
_ => throw new SerializationException(UnexpectedTypeDecodingMessage(reader.CurrentTokenType.GetFaunaType()))
};

public override void Serialize(MappingContext context, Utf8FaunaWriter writer, object? o)
{
switch (o)
{
case null:
writer.WriteNullValue();
break;
case byte[] b:
writer.WriteBytesValue(b);
break;
default:
throw new SerializationException(UnsupportedSerializationTypeMessage(o.GetType()));
}
}

public override List<FaunaType> GetSupportedTypes() => new List<FaunaType> { FaunaType.Bytes, FaunaType.Null };
}

internal class SByteSerializer : BaseSerializer<sbyte>
{
public override sbyte Deserialize(MappingContext context, ref Utf8FaunaReader reader) =>
Expand Down
7 changes: 4 additions & 3 deletions Fauna/Serialization/TokenType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public enum TokenType

/// <summary>The token type is a Fauna string.</summary>
String,
/// <summary>The token type is a Fauna byte array.</summary>
Bytes,

/// <summary>The token type is a Fauna integer.</summary>
Int,
Expand Down Expand Up @@ -94,9 +96,8 @@ public static FaunaType GetFaunaType(this TokenType tokenType)

case TokenType.String:
return FaunaType.String;
// BUG: @bytes support is missing; BT-5183
// case TokenType.Bytes:
// return FaunaType.Bytes;
case TokenType.Bytes:
return FaunaType.Bytes;
case TokenType.Int:
return FaunaType.Int;
case TokenType.Long:
Expand Down
22 changes: 22 additions & 0 deletions Fauna/Serialization/Utf8FaunaReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ public bool Read()
TokenType.Time => GetTime(),
TokenType.True or TokenType.False => GetBoolean(),
TokenType.Module => GetModule(),
TokenType.Bytes => GetBytes(),
_ => throw new SerializationException($"{CurrentTokenType} does not have an associated value")
};
}
Expand Down Expand Up @@ -301,6 +302,24 @@ public byte GetByte()
}
}

/// <summary>
/// Retrieves a byte array value from the current token.
/// </summary>
/// <returns>A byte array representation of the current token's value.</returns>
public byte[] GetBytes()
{
ValidateTaggedTypes(TokenType.Bytes);

try
{
return Convert.FromBase64String(_taggedTokenValue!);
}
catch (Exception e)
{
throw new SerializationException($"Failed to get byte array from {_taggedTokenValue}", e);
}
}

/// <summary>
/// Retrieves an unsigned byte value from the current token.
/// </summary>
Expand Down Expand Up @@ -584,6 +603,9 @@ private void HandleStartObject()
case "@time":
HandleTaggedString(TokenType.Time);
break;
case "@bytes":
HandleTaggedString(TokenType.Bytes);
break;
default:
_bufferedTokenType = TokenType.FieldName;
_tokenStack.Push(TokenType.StartObject);
Expand Down
20 changes: 20 additions & 0 deletions Fauna/Serialization/Utf8FaunaWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,17 @@ public void WriteLong(string fieldName, long value)
WriteLongValue(value);
}

/// <summary>
/// Writes a byte array value with a specific field name.
/// </summary>
/// <param name="fieldName">The name of the field.</param>
/// <param name="value">The byte array value to write.</param>
public void WriteBytes(string fieldName, byte[] value)
{
WriteFieldName(fieldName);
WriteBytesValue(value);
}

/// <summary>
/// Writes a string value with a specific field name.
/// </summary>
Expand Down Expand Up @@ -291,6 +302,15 @@ public void WriteIntValue(int value)
WriteTaggedValue("@int", value.ToString());
}

/// <summary>
/// Writes a byte array value as a tagged element.
/// </summary>
/// <param name="value">The byte array value to write.</param>
public void WriteBytesValue(byte[] value)
{
WriteTaggedValue("@bytes", Convert.ToBase64String(value));
}

/// <summary>
/// Writes a long integer value as a tagged element.
/// </summary>
Expand Down

0 comments on commit c1b452e

Please sign in to comment.