This is an automated email from the ASF dual-hosted git repository. valentyn pushed a commit to branch valentyn/dotnet-poc in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit fabe879c704140f805268877d62bd898125f9e95 Author: Valentyn Kahamlyk <[email protected]> AuthorDate: Mon Jul 29 16:01:05 2024 -0700 draft --- gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj | 2 +- .../Structure/IO/GraphBinary/DataType.cs | 7 ++- .../IO/GraphBinary/ResponseMessageSerializer.cs | 41 +++++++++----- .../IO/GraphBinary/TypeSerializerRegistry.cs | 1 + .../IO/GraphBinary/Types/SingleTypeSerializer.cs | 7 +++ gremlin-dotnet/src/Gremlin.Net/Structure/Marker.cs | 38 +++++++++++++ .../Driver/FakeStream.cs | 66 ++++++++++++++++++++++ .../Driver/GremlinClientTests.cs | 33 +++++++++++ .../util/ser/binary/MessageSerializerV4Test.java | 22 ++++++++ 9 files changed, 200 insertions(+), 17 deletions(-) diff --git a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj index 1ccfb182e8..272c669e47 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj +++ b/gremlin-dotnet/src/Gremlin.Net/Gremlin.Net.csproj @@ -74,7 +74,7 @@ NOTE that versions suffixed with "-rc" are considered release candidates (i.e. p <ItemGroup> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" /> - <PackageReference Include="System.Text.Json" Version="8.0.3" /> + <PackageReference Include="System.Text.Json" Version="8.0.4" /> <PackageReference Include="Polly" Version="8.4.0" /> </ItemGroup> diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs index 6ccad02937..ee812d1fbf 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/DataType.cs @@ -84,7 +84,12 @@ namespace Gremlin.Net.Structure.IO.GraphBinary /// A custom type, represented as a blob value. /// </summary> public static readonly DataType Custom = new DataType(0); - + + /// <summary> + /// + /// </summary> + public static readonly DataType Marker = new DataType(0xFD); + /// <summary> /// A null value for an unspecified Object value. /// </summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/ResponseMessageSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/ResponseMessageSerializer.cs index e31c7a22a8..93db388075 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/ResponseMessageSerializer.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/ResponseMessageSerializer.cs @@ -54,24 +54,35 @@ namespace Gremlin.Net.Structure.IO.GraphBinary throw new IOException("The most significant bit should be set according to the format"); } - var requestId = - (Guid?)await reader.ReadNullableValueAsync<Guid>(stream, cancellationToken).ConfigureAwait(false); - var code = (ResponseStatusCode)await reader.ReadNonNullableValueAsync<int>(stream, cancellationToken) - .ConfigureAwait(false); - var message = (string?)await reader.ReadNullableValueAsync<string>(stream, cancellationToken) - .ConfigureAwait(false); - var dictObj = await reader - .ReadNonNullableValueAsync<Dictionary<string, object>>(stream, cancellationToken).ConfigureAwait(false); - var attributes = (Dictionary<string, object>)dictObj; + //var requestId = + // (Guid?)await reader.ReadNullableValueAsync<Guid>(stream, cancellationToken).ConfigureAwait(false); + //var code = (ResponseStatusCode)await reader.ReadNonNullableValueAsync<int>(stream, cancellationToken) + // .ConfigureAwait(false); + //var message = (string?)await reader.ReadNullableValueAsync<string>(stream, cancellationToken) + // .ConfigureAwait(false); + //var dictObj = await reader + // .ReadNonNullableValueAsync<Dictionary<string, object>>(stream, cancellationToken).ConfigureAwait(false); + //var attributes = (Dictionary<string, object>)dictObj; - var status = new ResponseStatus(code, attributes, message); + var status = new ResponseStatus(ResponseStatusCode.Success, new Dictionary<string, object>(), "hello"); - var meta = (Dictionary<string, object>)await reader - .ReadNonNullableValueAsync<Dictionary<string, object>>(stream, cancellationToken).ConfigureAwait(false); - var data = (List<object>?)await reader.ReadAsync(stream, cancellationToken).ConfigureAwait(false); - var result = new ResponseResult<List<object>>(data, meta); + //var meta = (Dictionary<string, object>)await reader + // .ReadNonNullableValueAsync<Dictionary<string, object>>(stream, cancellationToken).ConfigureAwait(false); + //var data = (List<object>?)await reader.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + //var result = new ResponseResult<List<object>>(data, meta); - return new ResponseMessage<List<object>>(requestId, status, result); + var result = new List<object>(); + while (stream.Position != stream.Length) + { + var obj = await reader.ReadAsync(stream, cancellationToken).ConfigureAwait(false); + if (Marker.END_OF_STREAM == obj) + { + break; + } + result.Add(obj!); + } + + return new ResponseMessage<List<object>>(Guid.Empty, status, new ResponseResult<List<object>>(result, null)); } } } \ No newline at end of file diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs index e7f1997771..cbc1d63ff0 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/TypeSerializerRegistry.cs @@ -133,6 +133,7 @@ namespace Gremlin.Net.Structure.IO.GraphBinary {DataType.BulkSet, new BulkSetSerializer<List<object>>()}, {DataType.Char, new CharSerializer()}, {DataType.Duration, new DurationSerializer()}, + {DataType.Marker, SingleTypeSerializers.MarkerSerializer}, }; private readonly Dictionary<string, CustomTypeSerializer> _serializerByCustomTypeName = diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs index 04830cffba..e31c52d8bf 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/IO/GraphBinary/Types/SingleTypeSerializer.cs @@ -86,6 +86,13 @@ namespace Gremlin.Net.Structure.IO.GraphBinary.Types public static readonly SingleTypeSerializer<byte> ByteSerializer = new SingleTypeSerializer<byte>(DataType.Byte, (value, stream, cancellationToken) => stream.WriteByteAsync(value, cancellationToken), (stream, cancellationToken) => stream.ReadByteAsync(cancellationToken)); + + /// <summary> + /// + /// </summary> + public static readonly SingleTypeSerializer<Marker> MarkerSerializer = new SingleTypeSerializer<Marker>(DataType.Marker, + (value, stream, cancellationToken) => stream.WriteByteAsync(value.Value, cancellationToken), + async (stream, cancellationToken) => Marker.Of(await stream.ReadByteAsync(cancellationToken))); } /// <summary> diff --git a/gremlin-dotnet/src/Gremlin.Net/Structure/Marker.cs b/gremlin-dotnet/src/Gremlin.Net/Structure/Marker.cs new file mode 100644 index 0000000000..153279a9f9 --- /dev/null +++ b/gremlin-dotnet/src/Gremlin.Net/Structure/Marker.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Gremlin.Net.Structure +{ + + /// <summary> + /// + /// </summary> + public class Marker + { + /// <summary> + /// + /// </summary> + public byte Value { get; private set; } + /// <summary> + /// + /// </summary> + public static Marker END_OF_STREAM = new(0); + + private Marker(byte value) + { + Value = value; + } + + /// <summary> + /// + /// </summary> + /// <param name="value"></param> + /// <returns></returns> + public static Marker Of(byte value) + { + if (value != 0) throw new ArgumentException(); + return END_OF_STREAM; + } + } +} diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/FakeStream.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/FakeStream.cs new file mode 100644 index 0000000000..d6fb790c94 --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/FakeStream.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Gremlin.Net.Structure.IO.GraphBinary; + +namespace Gremlin.Net.IntegrationTest.Driver +{ + internal class FakeStream : Stream + { + private List<object> _results; + + public FakeStream(List<object> results) + { + _results = results; + } + + public override bool CanRead => throw new NotImplementedException(); + + public override bool CanSeek => throw new NotImplementedException(); + + public override bool CanWrite => true; + + public override long Length => throw new NotImplementedException(); + + public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public override void Flush() + { + throw new NotImplementedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotImplementedException(); + } + + public override void SetLength(long value) + { + throw new NotImplementedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotImplementedException(); + } + + public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) + { + var serializer = new GraphBinaryMessageSerializer(); + + var m = await serializer.DeserializeMessageAsync(buffer.ToArray()); + + // buffer.Span.ToArray().Select(a => a.ToString()) + _results.AddRange(m.Result.Data); + } + } +} diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs index 553547b8d4..fd8d42b2a9 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Driver/GremlinClientTests.cs @@ -23,6 +23,8 @@ using System; using System.Collections.Generic; +using System.Net.Http.Headers; +using System.Net.Http; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; @@ -91,6 +93,37 @@ namespace Gremlin.Net.IntegrationTest.Driver } } + [Fact] + public async Task Should() + { + try + { + var fakeMessage = new byte[] { 256 - 127, 0, 0, 0, 2, 3, 0, 0, 0, 0, 8, 98, 105, 110, 100, 105, 110, 103, 115, 10, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 8, 108, 97, 110, 103, 117, 97, 103, 101, 3, 0, 0, 0, 0, 14, 103, 114, 101, 109, 108, 105, 110, 45, 103, 114, 111, 111, 118, 121, 0, 0, 0, 13, 103, 46, 86, 40, 41, 46, 99, 111, 117, 110, 116, 40, 41 }; + + var url = "http://localhost:8182"; + var httpClient = new HttpClient(); + httpClient.DefaultRequestHeaders.Add("Accept", "application/vnd.graphbinary-v4.0"); + var request = new HttpRequestMessage(new HttpMethod("POST"), url); + request.Content = new ByteArrayContent(fakeMessage); + request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/vnd.graphbinary-v4.0"); + + var httpResponse = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + + var results = new List<object>(); + var logger = new FakeStream(results); + + using (var responseStream = await httpResponse.Content.ReadAsStreamAsync()) + { + await responseStream.CopyToAsync(logger); + } + + Console.WriteLine(results); + } catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + [Fact] public async Task ShouldThrowExceptionForInvalidScript() { diff --git a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerV4Test.java b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerV4Test.java index f160224cff..206bed5edc 100644 --- a/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerV4Test.java +++ b/gremlin-util/src/test/java/org/apache/tinkerpop/gremlin/util/ser/binary/MessageSerializerV4Test.java @@ -21,11 +21,13 @@ package org.apache.tinkerpop.gremlin.util.ser.binary; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.handler.codec.http.HttpResponseStatus; +import org.apache.commons.lang3.StringUtils; import org.apache.tinkerpop.gremlin.process.traversal.Traversal; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; import org.apache.tinkerpop.gremlin.util.MessageSerializerV4; import org.apache.tinkerpop.gremlin.util.TokensV4; +import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils; import org.apache.tinkerpop.gremlin.util.message.RequestMessageV4; import org.apache.tinkerpop.gremlin.util.message.ResponseMessageV4; import org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV4; @@ -40,6 +42,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; import static org.apache.tinkerpop.gremlin.util.MockitoHamcrestMatcherAdapter.reflectionEquals; import static org.hamcrest.MatcherAssert.assertThat; @@ -84,6 +88,24 @@ public class MessageSerializerV4Test { assertThat(request, reflectionEquals(deserialized)); } + @Test + public void test() throws SerializationException { + final RequestMessageV4 request = RequestMessageV4.build("g.V().count()").create(); + + final ByteBuf buffer = new GraphBinaryMessageSerializerV4().serializeRequestAsBinary(request, allocator); + byte[] arr = new byte[buffer.readableBytes()]; + buffer.readBytes(arr); + + StringBuilder sbStr = new StringBuilder(); + for (int i = 0, il = arr.length; i < il; i++) { + if (i > 0) + sbStr.append(","); + sbStr.append(arr[i]); + } + final String s = sbStr.toString(); + System.out.println(s); + } + @Test public void shouldSerializeAndDeserializeRequestWithoutArgs() throws SerializationException { final RequestMessageV4 request = RequestMessageV4.build("query").create();
