From 0e42da3f4a422c6b7aa61adf3acddf85cd1c49b5 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Wed, 23 Feb 2022 19:00:54 +0000 Subject: [PATCH 01/26] elementid --- .../Types/NativeToCypher.cs | 1 + .../ValueSerializers/ElementNodeSerializer.cs | 50 +++++++++++ .../ValueSerializers/ElementPathSerializer.cs | 90 +++++++++++++++++++ .../ElementRelationshipSerializer.cs | 39 ++++++++ .../ElementUnboundRelationshipSerializer.cs | 37 ++++++++ .../IO/ValueSerializers/PathSerializer.cs | 4 +- .../Protocol/BoltProtocolMessageFormat.cs | 2 + .../Internal/Protocol/BoltProtocolV5_0.cs | 2 + .../Protocol/BoltProtocolV5_0MessageFormat.cs | 36 ++++++++ .../Neo4j.Driver/Internal/Types/Node.cs | 34 ++++--- .../Internal/Types/Relationship.cs | 52 ++++++----- Neo4j.Driver/Neo4j.Driver/Types/IEntity.cs | 6 ++ .../Neo4j.Driver/Types/IRelationship.cs | 12 +++ 13 files changed, 331 insertions(+), 34 deletions(-) create mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs create mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs create mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs create mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs create mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0MessageFormat.cs diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs index 0a05d9697..0bf0ce132 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs @@ -146,6 +146,7 @@ public static NativeToCypherObject CypherNode(string cypherType, object obj) var cypherNode = new Dictionary { ["id"] = Convert(node.Id), + ["elementId"] = Convert(node.ElementId), ["labels"] = Convert(new List(node.Labels)), ["props"] = Convert(new Dictionary(node.Properties)) }; diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs new file mode 100644 index 000000000..650b5d155 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs @@ -0,0 +1,50 @@ +// Copyright (c) "Neo4j" +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using Neo4j.Driver.Internal.Types; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + internal class ElementNodeSerializer : ReadOnlySerializer + { + public const byte Node = (byte)'N'; + public override IEnumerable ReadableStructs => new[] {Node}; + + public override object Deserialize(IPackStreamReader reader, byte signature, long size) + { + var urn = reader.ReadString(); + + var numLabels = (int) reader.ReadListHeader(); + var labels = new List(numLabels); + for (var i = 0; i < numLabels; i++) + { + labels.Add(reader.ReadString()); + } + + var numProps = (int) reader.ReadMapHeader(); + var props = new Dictionary(numProps); + for (var j = 0; j < numProps; j++) + { + var key = reader.ReadString(); + props.Add(key, reader.Read()); + } + + return new Node(urn, labels, props); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs new file mode 100644 index 000000000..e56e344b7 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs @@ -0,0 +1,90 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using System.Linq; +using Neo4j.Driver.Internal.Types; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + internal class ElementPathSerializer : ReadOnlySerializer + { + public const byte Path = (byte)'P'; + public override IEnumerable ReadableStructs => new[] { Path }; + + public override object Deserialize(IPackStreamReader reader, byte signature, long size) + { + // List of unique nodes + var uniqNodes = new INode[(int)reader.ReadListHeader()]; + for (var i = 0; i < uniqNodes.Length; i++) + { + var node = reader.Read() as INode; + + Throw.ProtocolException.IfFalse(node != null, "receivedNode"); + + uniqNodes[i] = node; + } + + // List of unique relationships, without start/end information + var uniqRels = new Relationship[(int)reader.ReadListHeader()]; + for (var i = 0; i < uniqRels.Length; i++) + { + var uniqRel = reader.Read() as Relationship; + + Throw.ProtocolException.IfFalse(uniqRel != null, "receivedUnboundRelationship"); + + uniqRels[i] = uniqRel; + } + + // Path sequence + var length = (int)reader.ReadListHeader(); + + // Knowing the sequence length, we can create the arrays that will represent the nodes, rels and segments in their "path order" + var segments = new ISegment[length / 2]; + var nodes = new INode[segments.Length + 1]; + var rels = new IRelationship[segments.Length]; + + var prevNode = uniqNodes[0]; + nodes[0] = prevNode; + for (var i = 0; i < segments.Length; i++) + { + var relIdx = (int)reader.ReadLong(); + var nextNode = + uniqNodes[(int)reader.ReadLong()]; // Start node is always 0, and isn't encoded in the sequence + // Negative rel index means this rel was traversed "inversed" from its direction + Relationship rel; + if (relIdx < 0) + { + rel = uniqRels[(-relIdx) - 1]; // -1 because rel idx are 1-indexed + rel.SetStartAndEnd(nextNode.ElementId, prevNode.ElementId); + } + else + { + rel = uniqRels[relIdx - 1]; + rel.SetStartAndEnd(prevNode.ElementId, nextNode.ElementId); + } + + nodes[i + 1] = nextNode; + rels[i] = rel; + segments[i] = new Segment(prevNode, rel, nextNode); + prevNode = nextNode; + } + + return new Path(segments.ToList(), nodes.ToList(), rels.ToList()); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs new file mode 100644 index 000000000..4c8b272e3 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using Neo4j.Driver.Internal.Types; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + internal class ElementRelationshipSerializer : ReadOnlySerializer + { + public const byte Relationship = (byte)'R'; + public override IEnumerable ReadableStructs => new[] { Relationship }; + + public override object Deserialize(IPackStreamReader reader, byte signature, long size) + { + var urn = reader.ReadString(); + var startUrn = reader.ReadString(); + var endUrn = reader.ReadString(); + var relType = reader.ReadString(); + var props = reader.ReadMap(); + + return new Relationship(urn, startUrn, endUrn, relType, props); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs new file mode 100644 index 000000000..a6f15d3a9 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using Neo4j.Driver.Internal.Types; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + internal class ElementUnboundRelationshipSerializer : ReadOnlySerializer + { + public const byte UnboundRelationship = (byte)'r'; + public override IEnumerable ReadableStructs => new[] { UnboundRelationship }; + + public override object Deserialize(IPackStreamReader reader, byte signature, long size) + { + var urn = reader.ReadString(); + var relType = reader.ReadString(); + var props = reader.ReadMap(); + + return new Relationship(urn, "-1", "-1", relType, props); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs index cf706febe..bc1ff749a 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs @@ -72,12 +72,12 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon if (relIdx < 0) { rel = uniqRels[(-relIdx) - 1]; // -1 because rel idx are 1-indexed - rel.SetStartAndEnd(nextNode.Id, prevNode.Id); + rel.SetStartAndEnd(nextNode.ElementId, prevNode.ElementId); } else { rel = uniqRels[relIdx - 1]; - rel.SetStartAndEnd(prevNode.Id, nextNode.Id); + rel.SetStartAndEnd(prevNode.ElementId, nextNode.ElementId); } nodes[i + 1] = nextNode; diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolMessageFormat.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolMessageFormat.cs index ddfb31565..b70cb6945 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolMessageFormat.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolMessageFormat.cs @@ -32,5 +32,7 @@ internal static class BoltProtocolMessageFormat public static readonly IMessageFormat V4_3 = new BoltProtocolV4_3MessageFormat(); public static readonly IMessageFormat V4_4 = new BoltProtocolV4_4MessageFormat(); + + public static readonly IMessageFormat V5_0 = new BoltProtocolV5_0MessageFormat(); } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0.cs index f45483483..9c04c8fb2 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using Neo4j.Driver.Internal.Connector; +using Neo4j.Driver.Internal.IO; using Neo4j.Driver.Internal.MessageHandling; using Neo4j.Driver.Internal.MessageHandling.V5_0; @@ -25,6 +26,7 @@ namespace Neo4j.Driver.Internal.Protocol internal class BoltProtocolV5_0 : BoltProtocolV4_4 { public override BoltProtocolVersion Version => BoltProtocolVersion.V5_0; + protected override IMessageFormat MessageFormat => BoltProtocolMessageFormat.V5_0; public BoltProtocolV5_0(IDictionary routingContext) : base(routingContext) { diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0MessageFormat.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0MessageFormat.cs new file mode 100644 index 000000000..ec5cafcb9 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolV5_0MessageFormat.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using Neo4j.Driver.Internal.IO.ValueSerializers; + +namespace Neo4j.Driver.Internal.Protocol +{ + class BoltProtocolV5_0MessageFormat : BoltProtocolV4_4MessageFormat + { + public BoltProtocolV5_0MessageFormat() + { + RemoveHandler(); + AddHandler(); + + RemoveHandler(); + AddHandler(); + + RemoveHandler(); + AddHandler(); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs index 9d99f6e6c..fb289428d 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs @@ -14,34 +14,44 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Neo4j.Driver; namespace Neo4j.Driver.Internal.Types { internal class Node : INode { - public long Id { get; } + [Obsolete("Replaced with ElementId")] + public long Id => long.Parse(ElementId); + public string ElementId { get; } public IReadOnlyList Labels { get; } public IReadOnlyDictionary Properties { get; } public object this[string key] => Properties[key]; - public Node(long id, IReadOnlyList lables, IReadOnlyDictionary prop) + public Node(long id, IReadOnlyList labels, IReadOnlyDictionary prop) + { + ElementId = id.ToString(); + Labels = labels; + Properties = prop; + } + + public Node(string id, IReadOnlyList labels, IReadOnlyDictionary prop) { - Id = id; - Labels = lables; + ElementId = id; + Labels = labels; Properties = prop; } public bool Equals(INode other) { - if (other == null) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(Id, other.Id); + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return Equals(ElementId, other.ElementId); } public override bool Equals(object obj) @@ -51,7 +61,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return Id.GetHashCode(); + return ElementId.GetHashCode(); } } } diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs index a103b6773..e7d2f08af 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs @@ -14,39 +14,52 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Neo4j.Driver; namespace Neo4j.Driver.Internal.Types { internal class Relationship : IRelationship { - public long Id { get; } + public long Id => long.Parse(ElementId); + public string ElementId { get; } public string Type { get; } - public long StartNodeId { get; internal set; } - public long EndNodeId { get; internal set; } + public long StartNodeId => long.Parse(StartNodeElementId); + public long EndNodeId => long.Parse(EndNodeElementId); + + public string StartNodeElementId { get; internal set; } + public string EndNodeElementId { get; internal set; } public IReadOnlyDictionary Properties { get; } public object this[string key] => Properties[key]; - + public Relationship(long id, long startId, long endId, string relType, IReadOnlyDictionary props) { - Id = id; - StartNodeId = startId; - EndNodeId = endId; + ElementId = id.ToString(); + StartNodeElementId = startId.ToString(); + EndNodeElementId = endId.ToString(); + Type = relType; + Properties = props; + } + + public Relationship(string id, string startId, string endId, string relType, + IReadOnlyDictionary props) + { + ElementId = id; + StartNodeElementId = startId; + EndNodeElementId = endId; Type = relType; Properties = props; } public bool Equals(IRelationship other) { - if (other == null) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(Id, other.Id); + if (other == null) + return false; + + if (ReferenceEquals(this, other)) + return true; + + return Equals(ElementId, other.ElementId); } public override bool Equals(object obj) @@ -56,14 +69,13 @@ public override bool Equals(object obj) public override int GetHashCode() { - return Id.GetHashCode(); + return ElementId.GetHashCode(); } - internal void SetStartAndEnd(long start, long end) + internal void SetStartAndEnd(string start, string end) { - StartNodeId = start; - EndNodeId = end; + StartNodeElementId = start; + EndNodeElementId = end; } } - } diff --git a/Neo4j.Driver/Neo4j.Driver/Types/IEntity.cs b/Neo4j.Driver/Neo4j.Driver/Types/IEntity.cs index b99b8b1a7..7081cfe41 100644 --- a/Neo4j.Driver/Neo4j.Driver/Types/IEntity.cs +++ b/Neo4j.Driver/Neo4j.Driver/Types/IEntity.cs @@ -40,6 +40,12 @@ public interface IEntity /// /// Get the identity as a number. /// + [Obsolete("Replaced with ElementId, will be removed in 6.0")] long Id { get; } + + /// + /// Get the identity as a . + /// + string ElementId { get; } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs b/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs index 9756a0776..666bd3fab 100644 --- a/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs +++ b/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs @@ -33,11 +33,23 @@ public interface IRelationship : IEntity, IEquatable /// /// Gets the id of the start node of the relationship. /// + [Obsolete] long StartNodeId { get; } /// /// Gets the id of the end node of the relationship. /// + [Obsolete] long EndNodeId { get; } + + /// + /// Gets the ElementId of the start node of the relationship. + /// + string StartNodeElementId { get; } + + /// + /// Gets the ElementId of the end node of the relationship. + /// + string EndNodeElementId { get; } } } \ No newline at end of file From a76a9b0d542f5e784faa1f353f773e904327b5ad Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:19:27 +0000 Subject: [PATCH 02/26] implement element id --- .../ValueSerializers/ElementNodeSerializer.cs | 16 +++- .../ValueSerializers/ElementPathSerializer.cs | 90 ------------------- .../ElementRelationshipSerializer.cs | 15 +++- .../ElementUnboundRelationshipSerializer.cs | 14 ++- .../Neo4j.Driver/Internal/Types/Node.cs | 18 +++- .../Internal/Types/Relationship.cs | 38 ++++++-- 6 files changed, 88 insertions(+), 103 deletions(-) delete mode 100644 Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs index 650b5d155..1d1587eeb 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs @@ -27,7 +27,9 @@ internal class ElementNodeSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { - var urn = reader.ReadString(); + var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + + var nodeId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); var numLabels = (int) reader.ReadListHeader(); var labels = new List(numLabels); @@ -44,7 +46,17 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon props.Add(key, reader.Read()); } - return new Node(urn, labels, props); + var stringId = reader.ReadString(); + + return includingLongs + ? new Node(nodeId.Value, stringId, labels, props) + : new Node(stringId, labels, props); + } + + private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T: struct + { + reader.ReadNull(); + return null; } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs deleted file mode 100644 index e56e344b7..000000000 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementPathSerializer.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2002-2022 "Neo4j," -// Neo4j Sweden AB [http://neo4j.com] -// -// This file is part of Neo4j. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using System.Collections.Generic; -using System.Linq; -using Neo4j.Driver.Internal.Types; - -namespace Neo4j.Driver.Internal.IO.ValueSerializers -{ - internal class ElementPathSerializer : ReadOnlySerializer - { - public const byte Path = (byte)'P'; - public override IEnumerable ReadableStructs => new[] { Path }; - - public override object Deserialize(IPackStreamReader reader, byte signature, long size) - { - // List of unique nodes - var uniqNodes = new INode[(int)reader.ReadListHeader()]; - for (var i = 0; i < uniqNodes.Length; i++) - { - var node = reader.Read() as INode; - - Throw.ProtocolException.IfFalse(node != null, "receivedNode"); - - uniqNodes[i] = node; - } - - // List of unique relationships, without start/end information - var uniqRels = new Relationship[(int)reader.ReadListHeader()]; - for (var i = 0; i < uniqRels.Length; i++) - { - var uniqRel = reader.Read() as Relationship; - - Throw.ProtocolException.IfFalse(uniqRel != null, "receivedUnboundRelationship"); - - uniqRels[i] = uniqRel; - } - - // Path sequence - var length = (int)reader.ReadListHeader(); - - // Knowing the sequence length, we can create the arrays that will represent the nodes, rels and segments in their "path order" - var segments = new ISegment[length / 2]; - var nodes = new INode[segments.Length + 1]; - var rels = new IRelationship[segments.Length]; - - var prevNode = uniqNodes[0]; - nodes[0] = prevNode; - for (var i = 0; i < segments.Length; i++) - { - var relIdx = (int)reader.ReadLong(); - var nextNode = - uniqNodes[(int)reader.ReadLong()]; // Start node is always 0, and isn't encoded in the sequence - // Negative rel index means this rel was traversed "inversed" from its direction - Relationship rel; - if (relIdx < 0) - { - rel = uniqRels[(-relIdx) - 1]; // -1 because rel idx are 1-indexed - rel.SetStartAndEnd(nextNode.ElementId, prevNode.ElementId); - } - else - { - rel = uniqRels[relIdx - 1]; - rel.SetStartAndEnd(prevNode.ElementId, nextNode.ElementId); - } - - nodes[i + 1] = nextNode; - rels[i] = rel; - segments[i] = new Segment(prevNode, rel, nextNode); - prevNode = nextNode; - } - - return new Path(segments.ToList(), nodes.ToList(), rels.ToList()); - } - } -} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs index 4c8b272e3..f82c4788b 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs @@ -27,13 +27,26 @@ internal class ElementRelationshipSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { + var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + var relId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); + var relStartId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); + var relEndId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); + var urn = reader.ReadString(); var startUrn = reader.ReadString(); var endUrn = reader.ReadString(); var relType = reader.ReadString(); var props = reader.ReadMap(); - return new Relationship(urn, startUrn, endUrn, relType, props); + return includingLongs + ? new Relationship(relId.Value, urn, relStartId.Value, relEndId.Value, startUrn, endUrn, relType, props) + : new Relationship(urn, startUrn, endUrn, relType, props); + } + + private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T : struct + { + reader.ReadNull(); + return null; } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs index a6f15d3a9..56963e4ff 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs @@ -27,11 +27,23 @@ internal class ElementUnboundRelationshipSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { + var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + + var relId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); + var urn = reader.ReadString(); var relType = reader.ReadString(); var props = reader.ReadMap(); - return new Relationship(urn, "-1", "-1", relType, props); + return includingLongs + ? new Relationship(relId.Value, urn, -1, -1, "-1", "-1", relType, props) + : new Relationship(urn, "-1", "-1", relType, props); + } + + private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T : struct + { + reader.ReadNull(); + return null; } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs index fb289428d..1474805af 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Node.cs @@ -22,8 +22,9 @@ namespace Neo4j.Driver.Internal.Types { internal class Node : INode { - [Obsolete("Replaced with ElementId")] - public long Id => long.Parse(ElementId); + [Obsolete("Replaced with ElementId, Will be removed in 6.0")] + public long Id { get; } = -1; + public string ElementId { get; } public IReadOnlyList Labels { get; } public IReadOnlyDictionary Properties { get; } @@ -31,14 +32,23 @@ internal class Node : INode public Node(long id, IReadOnlyList labels, IReadOnlyDictionary prop) { + Id = id; ElementId = id.ToString(); Labels = labels; Properties = prop; } - public Node(string id, IReadOnlyList labels, IReadOnlyDictionary prop) + public Node(string elementId, IReadOnlyList labels, IReadOnlyDictionary prop) + { + ElementId = elementId; + Labels = labels; + Properties = prop; + } + + public Node(long id, string elementId, IReadOnlyList labels, IReadOnlyDictionary prop) { - ElementId = id; + Id = id; + ElementId = elementId; Labels = labels; Properties = prop; } diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs index e7d2f08af..be2329219 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs @@ -14,18 +14,24 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + +using System; using System.Collections.Generic; namespace Neo4j.Driver.Internal.Types { internal class Relationship : IRelationship { - public long Id => long.Parse(ElementId); - public string ElementId { get; } - public string Type { get; } - public long StartNodeId => long.Parse(StartNodeElementId); - public long EndNodeId => long.Parse(EndNodeElementId); + [Obsolete("Replaced by ElementId, Will be removed in 6.0")] + public long Id { get; } = -1; + [Obsolete("Replaced by StartNodeElementId, Will be removed in 6.0")] + public long StartNodeId { get; } = -1; + [Obsolete("Replaced by EndNodeElementId, Will be removed in 6.0")] + public long EndNodeId { get; } = -1; + public string Type { get; } + + public string ElementId { get; } public string StartNodeElementId { get; internal set; } public string EndNodeElementId { get; internal set; } public IReadOnlyDictionary Properties { get; } @@ -34,6 +40,10 @@ internal class Relationship : IRelationship public Relationship(long id, long startId, long endId, string relType, IReadOnlyDictionary props) { + Id = id; + StartNodeId = startId; + EndNodeId = endId; + ElementId = id.ToString(); StartNodeElementId = startId.ToString(); EndNodeElementId = endId.ToString(); @@ -47,10 +57,28 @@ internal class Relationship : IRelationship ElementId = id; StartNodeElementId = startId; EndNodeElementId = endId; + Type = relType; Properties = props; } + public Relationship(long id, string elementId, long startId, long endId, string startElementId, string endElementId, + string relType, + IReadOnlyDictionary props) + { + Id = id; + StartNodeId = startId; + EndNodeId = endId; + + ElementId = elementId; + StartNodeElementId = startElementId; + EndNodeElementId = endElementId; + + Type = relType; + Properties = props; + } + + public bool Equals(IRelationship other) { if (other == null) From e2c3586dd22fa184604466174c9ecb1215143cdf Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:22:30 +0000 Subject: [PATCH 03/26] move relationships elementid reading --- .../IO/ValueSerializers/ElementRelationshipSerializer.cs | 5 +++-- .../ValueSerializers/ElementUnboundRelationshipSerializer.cs | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs index f82c4788b..2597addfa 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs @@ -32,11 +32,12 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon var relStartId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); var relEndId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); + var relType = reader.ReadString(); + var props = reader.ReadMap(); + var urn = reader.ReadString(); var startUrn = reader.ReadString(); var endUrn = reader.ReadString(); - var relType = reader.ReadString(); - var props = reader.ReadMap(); return includingLongs ? new Relationship(relId.Value, urn, relStartId.Value, relEndId.Value, startUrn, endUrn, relType, props) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs index 56963e4ff..6da9ac5b9 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs @@ -31,10 +31,11 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon var relId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); - var urn = reader.ReadString(); var relType = reader.ReadString(); var props = reader.ReadMap(); + var urn = reader.ReadString(); + return includingLongs ? new Relationship(relId.Value, urn, -1, -1, "-1", "-1", relType, props) : new Relationship(urn, "-1", "-1", relType, props); From 38836fc6eb15cb29eecff17475c957154da26bf0 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:24:06 +0000 Subject: [PATCH 04/26] dedupe some code --- .../Neo4j.Driver/Internal/IO/ReadOnlySerializer.cs | 7 ++++++- .../Internal/IO/ValueSerializers/ElementNodeSerializer.cs | 6 ------ .../IO/ValueSerializers/ElementRelationshipSerializer.cs | 6 ------ .../ElementUnboundRelationshipSerializer.cs | 6 ------ 4 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ReadOnlySerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ReadOnlySerializer.cs index fdc2a8a77..830d7171f 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ReadOnlySerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ReadOnlySerializer.cs @@ -18,7 +18,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Neo4j.Driver; namespace Neo4j.Driver.Internal.IO { @@ -35,5 +34,11 @@ public void Serialize(IPackStreamWriter writer, object value) public abstract IEnumerable ReadableStructs { get; } public abstract object Deserialize(IPackStreamReader reader, byte signature, long size); + + protected static T? ReadNullAndReturnNull(IPackStreamReader reader) where T : struct + { + reader.ReadNull(); + return null; + } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs index 1d1587eeb..308ea2d85 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs @@ -52,11 +52,5 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon ? new Node(nodeId.Value, stringId, labels, props) : new Node(stringId, labels, props); } - - private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T: struct - { - reader.ReadNull(); - return null; - } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs index 2597addfa..5c4382516 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs @@ -43,11 +43,5 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon ? new Relationship(relId.Value, urn, relStartId.Value, relEndId.Value, startUrn, endUrn, relType, props) : new Relationship(urn, startUrn, endUrn, relType, props); } - - private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T : struct - { - reader.ReadNull(); - return null; - } } } \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs index 6da9ac5b9..0d9baf490 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs @@ -40,11 +40,5 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon ? new Relationship(relId.Value, urn, -1, -1, "-1", "-1", relType, props) : new Relationship(urn, "-1", "-1", relType, props); } - - private static T? ReadNullAndReturnNull(IPackStreamReader reader) where T : struct - { - reader.ReadNull(); - return null; - } } } \ No newline at end of file From e09a46bbbbf640af9757803facf5f9212ee798c3 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 11:41:35 +0000 Subject: [PATCH 05/26] split out entity tests --- .../Neo4j.Driver.Tests/EntityTests.cs | 104 ------------------ Neo4j.Driver/Neo4j.Driver.Tests/NodeTests.cs | 57 ++++++++++ Neo4j.Driver/Neo4j.Driver.Tests/PathTests.cs | 48 ++++++++ .../Neo4j.Driver.Tests/RelationshipTests.cs | 50 +++++++++ 4 files changed, 155 insertions(+), 104 deletions(-) delete mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/EntityTests.cs create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/NodeTests.cs create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/PathTests.cs create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/EntityTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/EntityTests.cs deleted file mode 100644 index 278eabaa5..000000000 --- a/Neo4j.Driver/Neo4j.Driver.Tests/EntityTests.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) "Neo4j" -// Neo4j Sweden AB [http://neo4j.com] -// -// This file is part of Neo4j. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -using FluentAssertions; -using Moq; -using Neo4j.Driver.Internal; -using Neo4j.Driver.Internal.Types; -using Neo4j.Driver; -using Xunit; - -namespace Neo4j.Driver.Tests -{ - public class EntityTests - { - public class NodeTests - { - [Fact] - public void ShouldEqualIfIdEquals() - { - var node1 = new Node(123, new []{"buibui"}, null); - var node2 = new Node(123, new []{"lala"}, null); - node1.Equals(node2).Should().BeTrue(); - Equals(node1, node2).Should().BeTrue(); - node1.GetHashCode().Should().Be(node2.GetHashCode()); - - var node3Mock = new Mock(); - node3Mock.Setup(f => f.Id).Returns(123); - node3Mock.Setup(f => f.Labels).Returns(new[] { "same interface, different implementation" }); - node3Mock.Setup(f => f.GetHashCode()).Returns(123); - var node3 = node3Mock.Object; - node1.Equals(node3).Should().BeTrue(); - Equals(node1, node3).Should().BeTrue(); - // TODO: The following test is currently not supported by Moq - //node1.GetHashCode().Should().Be(node3.GetHashCode()); - } - } - - public class RelationshipTests - { - [Fact] - public void ShouldEqualIfIdEquals() - { - var rel1 = new Relationship(123, 000, 111, "buibui", null); - var rel2 = new Relationship(123, 222, 333, "lala", null); - rel1.Equals(rel2).Should().BeTrue(); - Equals(rel1, rel2).Should().BeTrue(); - rel1.GetHashCode().Should().Be(rel2.GetHashCode()); - - var rel3Mock = new Mock(); - rel3Mock.Setup(f => f.Id).Returns(123); - rel3Mock.Setup(f => f.StartNodeId).Returns(444); - rel3Mock.Setup(f => f.EndNodeId).Returns(555); - rel3Mock.Setup(f => f.Type).Returns("same interface, different implementation"); - rel3Mock.Setup(f => f.GetHashCode()).Returns(123); - var rel3 = rel3Mock.Object; - - rel1.Equals(rel3).Should().BeTrue(); - Equals(rel1, rel3).Should().BeTrue(); - // TODO: The following test is currently not supported by Moq - //rel1.GetHashCode().Should().Be(rel3.GetHashCode()); - } - } - - public class PathTests - { - [Fact] - public void ShouldEqualIfIdEquals() - { - var path1 = new Path(null, - new []{ new Node(123, new []{"buibui"}, null) }, - new []{ new Relationship(1, 000, 111, "buibui", null)}); - var path2 = new Path(null, - new[] { new Node(123, new[] { "lala" }, null) }, - new []{ new Relationship(1, 222, 333, "lala", null)}); - path1.Equals(path2).Should().BeTrue(); - Equals(path1, path2).Should().BeTrue(); - path1.GetHashCode().Should().Be(path2.GetHashCode()); - - var path3Mock = new Mock(); - path3Mock.Setup(f => f.Start).Returns(new Node(123, new[] { "same interface, different implementation" }, null)); - path3Mock.Setup(f => f.Relationships).Returns(new[] { new Relationship(1, 222, 333, "same interface --- different implementation", null) }); - var path3 = path3Mock.Object; - path1.Equals(path3).Should().BeTrue(); - Equals(path1, path3).Should().BeTrue(); - - // TODO: The following test is currently not supported by Moq - //path1.GetHashCode().Should().Be(path2.GetHashCode()); - } - } - } -} diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/NodeTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/NodeTests.cs new file mode 100644 index 000000000..f002b71e6 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/NodeTests.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using FluentAssertions; +using Moq; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Tests +{ + public class NodeTests + { + [Fact] + public void ShouldEqualIfIdEquals() + { + var node1 = new Node(123, new[] { "buibui" }, null); + var node2 = new Node(123, new[] { "lala" }, null); + + node1.Equals(node2).Should().BeTrue(); + Equals(node1, node2).Should().BeTrue(); + + Dictionary nodes = new Dictionary(); + nodes.Add(node1, 123); + + nodes.TryGetValue(node2, out var value).Should().BeTrue(); + value.Should().Be(123); + + var node3Mock = new Mock(); + node3Mock.Setup(f => f.Id).Returns(123); + node3Mock.Setup(f => f.ElementId).Returns("123"); + node3Mock.Setup(f => f.Labels) + .Returns(new[] + { + "same interface, different implementation" + }); + var node3 = node3Mock.Object; + + node1.Equals(node3).Should().BeTrue(); + Equals(node1, node3).Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/PathTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/PathTests.cs new file mode 100644 index 000000000..83c0e0494 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/PathTests.cs @@ -0,0 +1,48 @@ +// Copyright (c) "Neo4j" +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using FluentAssertions; +using Moq; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Tests +{ + public class PathTests + { + [Fact] + public void ShouldEqualIfIdEquals() + { + var path1 = new Path(null, + new[] { new Node(123, new[] { "buibui" }, null) }, + new[] { new Relationship(1, 000, 111, "buibui", null) }); + var path2 = new Path(null, + new[] { new Node(123, new[] { "lala" }, null) }, + new[] { new Relationship(1, 222, 333, "lala", null) }); + path1.Equals(path2).Should().BeTrue(); + Equals(path1, path2).Should().BeTrue(); + path1.GetHashCode().Should().Be(path2.GetHashCode()); + + var path3Mock = new Mock(); + path3Mock.Setup(f => f.Start).Returns(new Node(123, new[] { "same interface, different implementation" }, null)); + path3Mock.Setup(f => f.Relationships).Returns(new[] { new Relationship(1, 222, 333, "same interface --- different implementation", null) }); + var path3 = path3Mock.Object; + path1.Equals(path3).Should().BeTrue(); + Equals(path1, path3).Should().BeTrue(); + } + } +} diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs new file mode 100644 index 000000000..6da5d9152 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using FluentAssertions; +using Moq; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Tests +{ + public class RelationshipTests + { + [Fact] + public void ShouldEqualIfIdEquals() + { + var rel1 = new Relationship(123, 000, 111, "buibui", null); + var rel2 = new Relationship(123, 222, 333, "lala", null); + rel1.Equals(rel2).Should().BeTrue(); + Equals(rel1, rel2).Should().BeTrue(); + rel1.GetHashCode().Should().Be(rel2.GetHashCode()); + + var rel3Mock = new Mock(); + rel3Mock.Setup(f => f.Id).Returns(123); + rel3Mock.Setup(f => f.StartNodeId).Returns(444); + rel3Mock.Setup(f => f.EndNodeId).Returns(555); + rel3Mock.Setup(f => f.Type).Returns("same interface, different implementation"); + rel3Mock.Setup(f => f.GetHashCode()).Returns(123); + var rel3 = rel3Mock.Object; + + rel1.Equals(rel3).Should().BeTrue(); + Equals(rel1, rel3).Should().BeTrue(); + // TODO: The following test is currently not supported by Moq + //rel1.GetHashCode().Should().Be(rel3.GetHashCode()); + } + } +} \ No newline at end of file From 929fb145cf1aa885a4f8528ff813df021f91ee98 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:23:54 +0000 Subject: [PATCH 06/26] fix path tests --- .../Internal/IO/ValueSerializers/PathSerializer.cs | 4 ++-- .../Neo4j.Driver/Internal/Types/Relationship.cs | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs index bc1ff749a..12ee52c80 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/PathSerializer.cs @@ -72,12 +72,12 @@ public override object Deserialize(IPackStreamReader reader, byte signature, lon if (relIdx < 0) { rel = uniqRels[(-relIdx) - 1]; // -1 because rel idx are 1-indexed - rel.SetStartAndEnd(nextNode.ElementId, prevNode.ElementId); + rel.SetStartAndEnd(nextNode, prevNode); } else { rel = uniqRels[relIdx - 1]; - rel.SetStartAndEnd(prevNode.ElementId, nextNode.ElementId); + rel.SetStartAndEnd(prevNode, nextNode); } nodes[i + 1] = nextNode; diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs index be2329219..8ff087fc8 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Types/Relationship.cs @@ -25,9 +25,9 @@ internal class Relationship : IRelationship [Obsolete("Replaced by ElementId, Will be removed in 6.0")] public long Id { get; } = -1; [Obsolete("Replaced by StartNodeElementId, Will be removed in 6.0")] - public long StartNodeId { get; } = -1; + public long StartNodeId { get; internal set; } = -1; [Obsolete("Replaced by EndNodeElementId, Will be removed in 6.0")] - public long EndNodeId { get; } = -1; + public long EndNodeId { get; internal set; } = -1; public string Type { get; } @@ -100,10 +100,12 @@ public override int GetHashCode() return ElementId.GetHashCode(); } - internal void SetStartAndEnd(string start, string end) + internal void SetStartAndEnd(INode start, INode end) { - StartNodeElementId = start; - EndNodeElementId = end; + StartNodeId = start.Id; + EndNodeId = end.Id; + StartNodeElementId = start.ElementId; + EndNodeElementId = end.ElementId; } } } From e1eeecd45c84e044f74177ed87f2c8f3cc9dc79b Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 12:51:56 +0000 Subject: [PATCH 07/26] 5.0 handshake for tk back end --- .../Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs | 2 +- .../Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs index 3c060977c..6ecbf8ede 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs @@ -28,7 +28,7 @@ static SupportedFeatures() "Feature:Bolt:4.2", "Feature:Bolt:4.3", "Feature:Bolt:4.4", - "Feature:Configuration:ConnectionAcquisitionTimeout", + "Feature:Bolt:5.0", "Feature:Impersonation", //"Feature:TLS:1.1", "Feature:TLS:1.2", diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index a135102eb..ed5df88c4 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -30,9 +30,9 @@ internal static class BoltProtocolFactory private static readonly int[] SupportedVersions = { + BoltProtocolVersion.V5_0.PackToInt(), BoltProtocolVersion.V4_4.PackToIntRange(BoltProtocolVersion.V4_2), BoltProtocolVersion.V4_1.PackToInt(), - BoltProtocolVersion.V4_0.PackToInt(), BoltProtocolVersion.V3_0.PackToInt() }; From 1bae742d51f2d44ff04264cfb97f014b577c8602 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 16:41:38 +0000 Subject: [PATCH 08/26] tidying --- .../Internal/Protocol/BoltProtocolFactory.cs | 63 ++++++------------- .../Internal/Protocol/BoltProtocolVersion.cs | 3 - 2 files changed, 19 insertions(+), 47 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index ed5df88c4..371e6955e 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -36,54 +36,29 @@ internal static class BoltProtocolFactory BoltProtocolVersion.V3_0.PackToInt() }; - public static IBoltProtocol ForVersion(BoltProtocolVersion version, IDictionary routingContext = null) { - if (version.Equals(3, 0)) - { - return new BoltProtocolV3(); - } - else if (version.Equals(4, 0)) - { - return new BoltProtocolV4_0(); - } - else if (version.Equals(4, 1) ) - { - return new BoltProtocolV4_1(routingContext); - } - else if (version.Equals(4, 2)) - { - return new BoltProtocolV4_2(routingContext); - } - else if (version.Equals(4, 3)) + return version switch { - return new BoltProtocolV4_3(routingContext); - } - else if (version.Equals(4, 4)) - { - return new BoltProtocolV4_4(routingContext); - } - else if (version.Equals(5, 0)) - { - return new BoltProtocolV5_0(routingContext); - } - else if(version.Equals(0, 0)) - { - throw new NotSupportedException( - "The Neo4j server does not support any of the protocol versions supported by this client. " + - "Ensure that you are using driver and server versions that are compatible with one another."); - } - else if (version == new BoltProtocolVersion(BoltHTTPIdentifier)) - { - throw new NotSupportedException( + {MajorVersion: 3, MinorVersion: 0} => new BoltProtocolV3(), + {MajorVersion: 4, MinorVersion: 0} => new BoltProtocolV4_0(), + {MajorVersion: 4, MinorVersion: 1} => new BoltProtocolV4_1(routingContext), + {MajorVersion: 4, MinorVersion: 2} => new BoltProtocolV4_2(routingContext), + {MajorVersion: 4, MinorVersion: 3} => new BoltProtocolV4_3(routingContext), + {MajorVersion: 4, MinorVersion: 4} => new BoltProtocolV4_4(routingContext), + {MajorVersion: 5, MinorVersion: 0} => new BoltProtocolV5_0(routingContext), + // 0.0 + {MajorVersion: 0, MinorVersion: 0} => throw new NotSupportedException( + "The Neo4j server does not support any of the protocol versions supported by this client. " + + "Ensure that you are using driver and server versions that are compatible with one another."), + // bolt + _ when version == new BoltProtocolVersion(BoltHTTPIdentifier) => throw new NotSupportedException( "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " + - $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"); - } - else - { - throw new NotSupportedException( - "Protocol error, server suggested unexpected protocol version: " + version.MajorVersion + "." + version.MinorVersion); - } + $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"), + //undefined + _ => throw new NotSupportedException( + "Protocol error, server suggested unexpected protocol version: " + version.MajorVersion + "." + version.MinorVersion) + }; } public static BoltProtocolVersion UnpackAgreedVersion(byte[] data) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolVersion.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolVersion.cs index 77126b4dc..0c9031464 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolVersion.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolVersion.cs @@ -112,8 +112,6 @@ public byte PackToByte() { return (byte)((MinorVersion << 4) | MajorVersion); } - - public override bool Equals(object obj) { @@ -230,5 +228,4 @@ public override string ToString() return $"{MajorVersion}.{MinorVersion}"; } } - } From 2c945d83e7f392deadd64b0dc9177a3657d3ad0b Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 16:55:23 +0000 Subject: [PATCH 09/26] tidy up strings --- .../Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index 371e6955e..396bd480f 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -57,7 +57,7 @@ public static IBoltProtocol ForVersion(BoltProtocolVersion version, IDictionary< $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"), //undefined _ => throw new NotSupportedException( - "Protocol error, server suggested unexpected protocol version: " + version.MajorVersion + "." + version.MinorVersion) + $"Protocol error, server suggested unexpected protocol version: {version}") }; } From 75ae9d2e254fca2acd392b891d710918cd5ced21 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:22:18 +0000 Subject: [PATCH 10/26] optimise handshake --- .../Internal/Connector/SocketClient.cs | 2 +- .../Internal/Protocol/BoltProtocolFactory.cs | 51 ++++++++----------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs index e198042bd..d862b88cb 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs @@ -150,7 +150,7 @@ public Task StopAsync() private async Task DoHandshakeAsync(CancellationToken cancellationToken = default) { - var data = BoltProtocolFactory.PackSupportedVersions(NumSupportedVersions); + var data = BoltProtocolFactory.PackSupportedVersions(); await _tcpSocketClient.WriteStream.WriteAsync(data, 0, data.Length, cancellationToken).ConfigureAwait(false); await _tcpSocketClient.WriteStream.FlushAsync(cancellationToken).ConfigureAwait(false); _logger?.Debug("C: [HANDSHAKE] {0}", data.ToHexString()); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index 396bd480f..a86d98d96 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -17,24 +17,34 @@ using System; using System.Collections.Generic; -using Neo4j.Driver.Internal.IO; using System.Linq; +using System.Threading; +using Neo4j.Driver.Internal.IO; namespace Neo4j.Driver.Internal.Protocol { internal static class BoltProtocolFactory { - //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') - private const int BoltIdentifier = 0x6060B017; private const int BoltHTTPIdentifier = 1213486160; //0x‭48 54 54 50 - or HTTP ascii codes... - private static readonly int[] SupportedVersions = - { - BoltProtocolVersion.V5_0.PackToInt(), - BoltProtocolVersion.V4_4.PackToIntRange(BoltProtocolVersion.V4_2), - BoltProtocolVersion.V4_1.PackToInt(), - BoltProtocolVersion.V3_0.PackToInt() - }; + /// + /// lazily evaluate handshake bytes once. + /// + private static readonly Lazy HandshakeBytesLazy = + new Lazy(() => + { + var versions = new int[] + { + //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') + 0x6060B017, + // 4 versions max. + BoltProtocolVersion.V5_0.PackToInt(), + BoltProtocolVersion.V4_4.PackToIntRange(BoltProtocolVersion.V4_2), + BoltProtocolVersion.V4_1.PackToInt(), + BoltProtocolVersion.V3_0.PackToInt() + }; + return versions.SelectMany(PackStreamBitConverter.GetBytes).ToArray(); + }, LazyThreadSafetyMode.PublicationOnly); public static IBoltProtocol ForVersion(BoltProtocolVersion version, IDictionary routingContext = null) { @@ -56,8 +66,7 @@ public static IBoltProtocol ForVersion(BoltProtocolVersion version, IDictionary< "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " + $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"), //undefined - _ => throw new NotSupportedException( - $"Protocol error, server suggested unexpected protocol version: {version}") + _ => throw new NotSupportedException($"Protocol error, server suggested unexpected protocol version: {version}") }; } @@ -66,22 +75,6 @@ public static BoltProtocolVersion UnpackAgreedVersion(byte[] data) return BoltProtocolVersion.FromPackedInt(PackStreamBitConverter.ToInt32(data)); } - public static byte[] PackSupportedVersions(int numVersionsToPack) - { - return PackVersions(SupportedVersions.Take(numVersionsToPack)); - } - - private static byte[] PackVersions(IEnumerable versions) - { - var aLittleBitOfMagic = PackStreamBitConverter.GetBytes(BoltIdentifier); - - var bytes = new List(aLittleBitOfMagic); - foreach (var version in versions) - { - bytes.AddRange(PackStreamBitConverter.GetBytes(version)); - } - - return bytes.ToArray(); - } + public static byte[] PackSupportedVersions() => HandshakeBytesLazy.Value; } } \ No newline at end of file From 25d6b63c08effa2181f145729f31a150d35fd8de Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:24:18 +0000 Subject: [PATCH 11/26] remove unused constant --- Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs index d862b88cb..ba6a1fee2 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Connector/SocketClient.cs @@ -31,7 +31,6 @@ namespace Neo4j.Driver.Internal.Connector { internal class SocketClient : ISocketClient { - private const int NumSupportedVersions = 4; private const string MessagePattern = "C: {0}"; private readonly Uri _uri; private readonly BufferSettings _bufferSettings; From 8897e8962462934bed01c0c21bc10f707e3f53b4 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 28 Feb 2022 17:44:01 +0000 Subject: [PATCH 12/26] name the constant --- .../Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index a86d98d96..726989328 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -26,17 +26,17 @@ namespace Neo4j.Driver.Internal.Protocol internal static class BoltProtocolFactory { private const int BoltHTTPIdentifier = 1213486160; //0x‭48 54 54 50 - or HTTP ascii codes... - /// /// lazily evaluate handshake bytes once. /// private static readonly Lazy HandshakeBytesLazy = new Lazy(() => { + const int goGoBolt = 0x6060B017; //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') + var versions = new int[] { - //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') - 0x6060B017, + goGoBolt, // 4 versions max. BoltProtocolVersion.V5_0.PackToInt(), BoltProtocolVersion.V4_4.PackToIntRange(BoltProtocolVersion.V4_2), From dd6f19067e5c3eabaaa273436221d5b15cb31745 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 1 Mar 2022 09:16:18 +0000 Subject: [PATCH 13/26] rejig the code again a bit --- .../Internal/Protocol/BoltProtocolFactory.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs index 726989328..d06609b03 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Protocol/BoltProtocolFactory.cs @@ -25,17 +25,21 @@ namespace Neo4j.Driver.Internal.Protocol { internal static class BoltProtocolFactory { - private const int BoltHTTPIdentifier = 1213486160; //0x‭48 54 54 50 - or HTTP ascii codes... - /// - /// lazily evaluate handshake bytes once. - /// + private const int BoltHttpIdentifier = 1213486160; //0x‭48 54 54 50 - or HTTP ascii codes... + static readonly string HttpErrorMessage = "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " + + $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"; + private static readonly string NoAgreedVersion = + "The Neo4j server does not support any of the protocol versions supported by this client. " + + "Ensure that you are using driver and server versions that are compatible with one another."; + private static readonly Lazy HandshakeBytesLazy = new Lazy(() => { - const int goGoBolt = 0x6060B017; //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') + const int goGoBolt = 0x6060B017; var versions = new int[] { + //This is a 'magic' handshake identifier to indicate we're using 'BOLT' ('GOGOBOLT') goGoBolt, // 4 versions max. BoltProtocolVersion.V5_0.PackToInt(), @@ -57,15 +61,11 @@ public static IBoltProtocol ForVersion(BoltProtocolVersion version, IDictionary< {MajorVersion: 4, MinorVersion: 3} => new BoltProtocolV4_3(routingContext), {MajorVersion: 4, MinorVersion: 4} => new BoltProtocolV4_4(routingContext), {MajorVersion: 5, MinorVersion: 0} => new BoltProtocolV5_0(routingContext), - // 0.0 - {MajorVersion: 0, MinorVersion: 0} => throw new NotSupportedException( - "The Neo4j server does not support any of the protocol versions supported by this client. " + - "Ensure that you are using driver and server versions that are compatible with one another."), - // bolt - _ when version == new BoltProtocolVersion(BoltHTTPIdentifier) => throw new NotSupportedException( - "Server responded HTTP. Make sure you are not trying to connect to the http endpoint " + - $"(HTTP defaults to port 7474 whereas BOLT defaults to port {GraphDatabase.DefaultBoltPort})"), - //undefined + // no matching versions + {MajorVersion: 0, MinorVersion: 0} => throw new NotSupportedException(NoAgreedVersion), + // http response + _ when version == new BoltProtocolVersion(BoltHttpIdentifier) => throw new NotSupportedException(HttpErrorMessage), + // undefined _ => throw new NotSupportedException($"Protocol error, server suggested unexpected protocol version: {version}") }; } From 599cf6ba9476fd0428086ddb1e3ded3873c154f0 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:41:00 +0000 Subject: [PATCH 14/26] fix a unit tests --- Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs index 6da5d9152..aaf59a4ac 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/RelationshipTests.cs @@ -34,9 +34,9 @@ public void ShouldEqualIfIdEquals() rel1.GetHashCode().Should().Be(rel2.GetHashCode()); var rel3Mock = new Mock(); - rel3Mock.Setup(f => f.Id).Returns(123); - rel3Mock.Setup(f => f.StartNodeId).Returns(444); - rel3Mock.Setup(f => f.EndNodeId).Returns(555); + rel3Mock.Setup(f => f.ElementId).Returns("123"); + rel3Mock.Setup(f => f.StartNodeElementId).Returns("444"); + rel3Mock.Setup(f => f.EndNodeElementId).Returns("555"); rel3Mock.Setup(f => f.Type).Returns("same interface, different implementation"); rel3Mock.Setup(f => f.GetHashCode()).Returns(123); var rel3 = rel3Mock.Object; From 4997962429be3cb5cf4a2ea14b46d6fb60c9797a Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 1 Mar 2022 10:58:56 +0000 Subject: [PATCH 15/26] fix backwards logic --- .../ElementNodeSerializerTests.cs | 97 +++++++++++++++++++ .../ValueSerializers/ElementNodeSerializer.cs | 2 +- .../ElementRelationshipSerializer.cs | 2 +- .../ElementUnboundRelationshipSerializer.cs | 2 +- 4 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs new file mode 100644 index 000000000..2e4aecf56 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs @@ -0,0 +1,97 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using FluentAssertions; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + public class ElementNodeSerializerTests : PackStreamSerializerTests + { + internal override IPackStreamSerializer SerializerUnderTest => new ElementNodeSerializer(); + + [Fact] + public void ShouldDeserialize() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, NodeSerializer.Node); + writer.Write(1); + writer.Write(new List { "Label1", "Label2" }); + writer.Write(new Dictionary + { + {"prop1", "something"}, + {"prop2", 15}, + {"prop3", true} + }); + writer.Write("1"); + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType().Which.Id.Should().Be(1L); + value.Should().BeOfType().Which.Labels.Should().Equal(new[] { "Label1", "Label2" }); + value.Should().BeOfType().Which.Properties.Should().HaveCount(3).And.Contain(new[] + { + new KeyValuePair("prop1", "something"), + new KeyValuePair("prop2", 15L), + new KeyValuePair("prop3", true), + }); + value.Should().BeOfType().Which.ElementId.Should().Be("1"); + } + + [Fact] + public void ShouldDeserializeWithNulls() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, NodeSerializer.Node); + writer.WriteNull(); + writer.Write(new List { "Label1", "Label2" }); + writer.Write(new Dictionary + { + {"prop1", "something"}, + {"prop2", 15}, + {"prop3", true} + }); + writer.Write("1"); + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType(); + var node = value.As(); + + node.ElementId.Should().Be("1"); + node.Id.Should().Be(-1L); + + node.Labels.Should().Equal(new[] { "Label1", "Label2" }); + node.Properties.Should().HaveCount(3).And.Contain(new[] + { + new KeyValuePair("prop1", "something"), + new KeyValuePair("prop2", 15L), + new KeyValuePair("prop3", true), + }); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs index 308ea2d85..668f47d4c 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementNodeSerializer.cs @@ -27,7 +27,7 @@ internal class ElementNodeSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { - var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + var includingLongs = reader.PeekNextType() != PackStream.PackType.Null; var nodeId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs index 5c4382516..6b7b81d3e 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementRelationshipSerializer.cs @@ -27,7 +27,7 @@ internal class ElementRelationshipSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { - var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + var includingLongs = reader.PeekNextType() != PackStream.PackType.Null; var relId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); var relStartId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); var relEndId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs index 0d9baf490..1799190f5 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializer.cs @@ -27,7 +27,7 @@ internal class ElementUnboundRelationshipSerializer : ReadOnlySerializer public override object Deserialize(IPackStreamReader reader, byte signature, long size) { - var includingLongs = reader.PeekNextType() == PackStream.PackType.Null; + var includingLongs = reader.PeekNextType() != PackStream.PackType.Null; var relId = includingLongs ? reader.ReadLong() : ReadNullAndReturnNull(reader); From 52bbd6200c69511d7237da8d350bdd31a96b628b Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 1 Mar 2022 20:26:39 +0000 Subject: [PATCH 16/26] implement cypher relationship mapping --- .../Types/NativeToCypher.cs | 20 ++++++++++++++++++- .../Internal/IO/PackStreamReader.cs | 3 --- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs index 0bf0ce132..2088579cd 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs @@ -39,7 +39,7 @@ internal static class NativeToCypher { typeof(Point), CypherTODO }, { typeof(INode), CypherNode }, - { typeof(IRelationship), CypherTODO }, + { typeof(IRelationship), CypherRelationship }, { typeof(IPath), CypherTODO } }; @@ -153,6 +153,24 @@ public static NativeToCypherObject CypherNode(string cypherType, object obj) return new NativeToCypherObject() { name = "Node", data = cypherNode }; } + + public static NativeToCypherObject CypherRelationship(string cypherType, object obj) + { + var rel = (IRelationship)obj; + var cypherRel = new Dictionary + { + ["id"] = Convert(rel.Id), + ["startNodeId"] = Convert(rel.StartNodeId), + ["type"] = Convert(rel.Type), + ["endNodeId"] = Convert(rel.EndNodeId), + ["props"] = Convert(new Dictionary(rel.Properties)), + ["elementId"] = Convert(rel.ElementId), + ["startNodeElementId"] = Convert(rel.StartNodeElementId), + ["endNodeElementId"] = Convert(rel.EndNodeElementId), + }; + + return new NativeToCypherObject() { name = "Relationship", data = cypherRel }; + } } } diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/IO/PackStreamReader.cs b/Neo4j.Driver/Neo4j.Driver/Internal/IO/PackStreamReader.cs index 845de6234..4dba2c689 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/IO/PackStreamReader.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/IO/PackStreamReader.cs @@ -17,9 +17,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Text; -using Neo4j.Driver; -using Neo4j.Driver.Internal; namespace Neo4j.Driver.Internal.IO { From 5433119ea28051b05a876ecae71f79ca711dc8b8 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Wed, 2 Mar 2022 09:44:22 +0000 Subject: [PATCH 17/26] restore feature --- .../Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs index 6ecbf8ede..3a9642a0a 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs @@ -28,7 +28,8 @@ static SupportedFeatures() "Feature:Bolt:4.2", "Feature:Bolt:4.3", "Feature:Bolt:4.4", - "Feature:Bolt:5.0", + "Feature:Bolt:5.0", + "Feature:Configuration:ConnectionAcquisitionTimeout", "Feature:Impersonation", //"Feature:TLS:1.1", "Feature:TLS:1.2", From 0b45029fa1c965deb73d3ef75d08e382a36b244b Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Thu, 3 Mar 2022 09:23:18 +0000 Subject: [PATCH 18/26] send path back --- .../Types/NativeToCypher.cs | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs index 2088579cd..f2ee8592b 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Types/NativeToCypher.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; namespace Neo4j.Driver.Tests.TestBackend { @@ -40,15 +41,13 @@ internal static class NativeToCypher { typeof(INode), CypherNode }, { typeof(IRelationship), CypherRelationship }, - { typeof(IPath), CypherTODO } + { typeof(IPath), CypherPath } }; public static object Convert(object sourceObject) { if (sourceObject is null) - { return new NativeToCypherObject { name = "CypherNull", data = { } }; - } if (sourceObject as List != null) return FunctionMap[typeof(List)]("CypherList", sourceObject); @@ -171,6 +170,18 @@ public static NativeToCypherObject CypherRelationship(string cypherType, object return new NativeToCypherObject() { name = "Relationship", data = cypherRel }; } + + public static NativeToCypherObject CypherPath(string cypherType, object obj) + { + var path = (IPath)obj; + var cypherPath = new Dictionary + { + ["nodes"] = Convert(path.Nodes.OfType().ToList()), + ["relationships"] = Convert(path.Relationships.OfType().ToList()) + }; + + return new NativeToCypherObject() { name = "Path", data = cypherPath }; + } } } From fe9e8983fc49d67e4533c777fcc81ed92b3a67b2 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Fri, 4 Mar 2022 11:48:50 +0000 Subject: [PATCH 19/26] add more unit tests --- .../ElementNodeSerializerTests.cs | 4 +- .../ElementRelationshipSerializerTests.cs | 105 ++++++++++++++++++ ...ementUnboundRelationshipSerializerTests.cs | 105 ++++++++++++++++++ 3 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementRelationshipSerializerTests.cs create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs index 2e4aecf56..3cd5b8136 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementNodeSerializerTests.cs @@ -32,7 +32,7 @@ public void ShouldDeserialize() var writerMachine = CreateWriterMachine(); var writer = writerMachine.Writer(); - writer.WriteStructHeader(3, NodeSerializer.Node); + writer.WriteStructHeader(3, ElementNodeSerializer.Node); writer.Write(1); writer.Write(new List { "Label1", "Label2" }); writer.Write(new Dictionary @@ -64,7 +64,7 @@ public void ShouldDeserializeWithNulls() var writerMachine = CreateWriterMachine(); var writer = writerMachine.Writer(); - writer.WriteStructHeader(3, NodeSerializer.Node); + writer.WriteStructHeader(3, ElementNodeSerializer.Node); writer.WriteNull(); writer.Write(new List { "Label1", "Label2" }); writer.Write(new Dictionary diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementRelationshipSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementRelationshipSerializerTests.cs new file mode 100644 index 000000000..389abc70e --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementRelationshipSerializerTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using FluentAssertions; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + public class ElementRelationshipSerializerTests : PackStreamSerializerTests + { + internal override IPackStreamSerializer SerializerUnderTest => new ElementRelationshipSerializer(); + + [Fact] + public void ShouldDeserialize() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); + writer.Write(1); + writer.Write(2); + writer.Write(3); + writer.Write("RELATES_TO"); + writer.Write(new Dictionary + { + {"prop3", true} + }); + writer.Write("r1"); + writer.Write("n1"); + writer.Write("n2"); + + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType().Which.Id.Should().Be(1L); + value.Should().BeOfType().Which.StartNodeId.Should().Be(2L); + value.Should().BeOfType().Which.EndNodeId.Should().Be(3L); + value.Should().BeOfType().Which.Type.Should().Be("RELATES_TO"); + value.Should().BeOfType().Which.Properties.Should() + .HaveCount(1).And.Contain(new[] + { + new KeyValuePair("prop3", true), + }); + value.Should().BeOfType().Which.ElementId.Should().Be("r1"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + } + + [Fact] + public void ShouldDeserializeWithNulls() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); + writer.WriteNull(); + writer.WriteNull(); + writer.WriteNull(); + writer.Write("RELATES_TO"); + writer.Write(new Dictionary + { + {"prop3", true} + }); + writer.Write("r1"); + writer.Write("n1"); + writer.Write("n2"); + + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType().Which.Id.Should().Be(-1L); + value.Should().BeOfType().Which.StartNodeId.Should().Be(-1L); + value.Should().BeOfType().Which.EndNodeId.Should().Be(-1L); + value.Should().BeOfType().Which.Type.Should().Be("RELATES_TO"); + value.Should().BeOfType().Which.Properties.Should() + .HaveCount(1).And.Contain(new[] + { + new KeyValuePair("prop3", true), + }); + value.Should().BeOfType().Which.ElementId.Should().Be("r1"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs new file mode 100644 index 000000000..d9d3b2a5d --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using FluentAssertions; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + public class ElementUnboundRelationshipSerializerTests : PackStreamSerializerTests + { + internal override IPackStreamSerializer SerializerUnderTest => new ElementUnboundRelationshipSerializer(); + + [Fact] + public void ShouldDeserialize() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); + writer.Write(1); + writer.Write(2); + writer.Write(3); + writer.Write("RELATES_TO"); + writer.Write(new Dictionary + { + {"prop3", true} + }); + writer.Write("r1"); + writer.Write("n1"); + writer.Write("n2"); + + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType().Which.Id.Should().Be(1L); + value.Should().BeOfType().Which.StartNodeId.Should().Be(2L); + value.Should().BeOfType().Which.EndNodeId.Should().Be(3L); + value.Should().BeOfType().Which.Type.Should().Be("RELATES_TO"); + value.Should().BeOfType().Which.Properties.Should() + .HaveCount(1).And.Contain(new[] + { + new KeyValuePair("prop3", true), + }); + value.Should().BeOfType().Which.ElementId.Should().Be("r1"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + } + + [Fact] + public void ShouldDeserializeWithNulls() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); + writer.WriteNull(); + writer.WriteNull(); + writer.WriteNull(); + writer.Write("RELATES_TO"); + writer.Write(new Dictionary + { + {"prop3", true} + }); + writer.Write("r1"); + writer.Write("n1"); + writer.Write("n2"); + + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + value.Should().NotBeNull(); + value.Should().BeOfType().Which.Id.Should().Be(-1L); + value.Should().BeOfType().Which.StartNodeId.Should().Be(-1L); + value.Should().BeOfType().Which.EndNodeId.Should().Be(-1L); + value.Should().BeOfType().Which.Type.Should().Be("RELATES_TO"); + value.Should().BeOfType().Which.Properties.Should() + .HaveCount(1).And.Contain(new[] + { + new KeyValuePair("prop3", true), + }); + value.Should().BeOfType().Which.ElementId.Should().Be("r1"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + } + } +} \ No newline at end of file From 83ade24741d3da8597c97d6d184329dacca032ec Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Fri, 4 Mar 2022 14:54:03 +0000 Subject: [PATCH 20/26] unit tests --- .../ElementPathSerializerTests.cs | 206 ++++++++++++++++++ ...ementUnboundRelationshipSerializerTests.cs | 24 +- 2 files changed, 214 insertions(+), 16 deletions(-) create mode 100644 Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementPathSerializerTests.cs diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementPathSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementPathSerializerTests.cs new file mode 100644 index 000000000..ac14ad142 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementPathSerializerTests.cs @@ -0,0 +1,206 @@ +// Copyright (c) 2002-2022 "Neo4j," +// Neo4j Sweden AB [http://neo4j.com] +// +// This file is part of Neo4j. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System.Collections.Generic; +using FluentAssertions; +using Neo4j.Driver.Internal.Types; +using Xunit; + +namespace Neo4j.Driver.Internal.IO.ValueSerializers +{ + public class ElementPathSerializerTests : PackStreamSerializerTests + { + internal override IPackStreamSerializer SerializerUnderTest => new PathSerializer(); + + internal override IEnumerable SerializersNeeded => + new IPackStreamSerializer[] { new ElementNodeSerializer(), new ElementUnboundRelationshipSerializer() }; + + + [Fact] + public void ShouldDeserializeAddingElementIds() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + SerializeElementPath(writer, + new List + { + new Node(1, new List{"a"}, new Dictionary()), + new Node(2, new List{"a"}, new Dictionary()), + }, + new List + { + new Relationship(1, -1, -1, "LIKES", new Dictionary()) + }, + new List + { + 1, 1 + }); + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + var path = value.Should().BeOfType(); + + path.Which.Nodes.Should().AllBeOfType(); + path.Which.Relationships.Should().AllBeOfType(); + + var nodes = path.Which.Nodes; + var relationships = path.Which.Relationships; + + + nodes[0].Id.Should().Be(1L); + nodes[0].ElementId.Should().Be("1"); + nodes[1].Id.Should().Be(2L); + nodes[1].ElementId.Should().Be("2"); + + relationships[0].Id.Should().Be(1); + relationships[0].ElementId.Should().Be("1"); + relationships[0].StartNodeId.Should().Be(1L); + relationships[0].StartNodeElementId.Should().Be("1"); + relationships[0].EndNodeId.Should().Be(2L); + relationships[0].EndNodeElementId.Should().Be("2"); + } + + [Fact] + public void ShouldDeserializeWithElementIds() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + SerializeElementPath(writer, + new List + { + new Node(1, "n1", new List{"a"}, new Dictionary()), + new Node(2, "n2",new List{"a"}, new Dictionary()), + }, + new List + { + new Relationship(1, "r1", -1, -1, "-1", "-1", "LIKES", new Dictionary()) + }, + new List + { + 1, 1 + }); + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + var path = value.Should().BeOfType(); + + path.Which.Nodes.Should().AllBeOfType(); + path.Which.Relationships.Should().AllBeOfType(); + + var nodes = path.Which.Nodes; + var relationships = path.Which.Relationships; + + nodes[0].Id.Should().Be(1L); + nodes[0].ElementId.Should().Be("n1"); + nodes[1].Id.Should().Be(2L); + nodes[1].ElementId.Should().Be("n2"); + + relationships[0].Id.Should().Be(1); + relationships[0].ElementId.Should().Be("r1"); + relationships[0].StartNodeId.Should().Be(1L); + relationships[0].StartNodeElementId.Should().Be("n1"); + relationships[0].EndNodeId.Should().Be(2L); + relationships[0].EndNodeElementId.Should().Be("n2"); + } + + [Fact] + public void ShouldDeserializeWithOnlyElementIds() + { + var writerMachine = CreateWriterMachine(); + var writer = writerMachine.Writer(); + + SerializeElementPath(writer, + new List + { + new Node(-1, "n1", new List{"a"}, new Dictionary()), + new Node(-1, "n2",new List{"a"}, new Dictionary()), + }, + new List + { + new Relationship(-1, "r1", -1, -1, "-1", "-1", "LIKES", new Dictionary()) + }, + new List + { + 1, 1 + }); + + var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); + var value = readerMachine.Reader().Read(); + + var path = value.Should().BeOfType(); + + path.Which.Nodes.Should().AllBeOfType(); + path.Which.Relationships.Should().AllBeOfType(); + + var nodes = path.Which.Nodes; + var relationships = path.Which.Relationships; + + nodes[0].Id.Should().Be(-1L); + nodes[0].ElementId.Should().Be("n1"); + nodes[1].Id.Should().Be(-1L); + nodes[1].ElementId.Should().Be("n2"); + + relationships[0].Id.Should().Be(-1L); + relationships[0].ElementId.Should().Be("r1"); + relationships[0].StartNodeId.Should().Be(-1L); + relationships[0].StartNodeElementId.Should().Be("n1"); + relationships[0].EndNodeId.Should().Be(-1L); + relationships[0].EndNodeElementId.Should().Be("n2"); + } + + private static void SerializeElementPath(IPackStreamWriter writer, List nodes, List rels, + List indicies) + { + writer.WriteStructHeader(3, PathSerializer.Path); + writer.WriteListHeader(nodes.Count); + + foreach (var node in nodes) + { + writer.WriteStructHeader(3, ElementNodeSerializer.Node); + + if (node.Id == -1) + writer.WriteNull(); + else + writer.Write(node.Id); + writer.Write(node.Labels); + writer.Write(node.Properties); + writer.Write(node.ElementId); + } + + writer.WriteListHeader(rels.Count); + + foreach (var rel in rels) + { + writer.WriteStructHeader(3, UnboundRelationshipSerializer.UnboundRelationship); + + if (rel.Id == -1) + writer.WriteNull(); + else + writer.Write(rel.Id); + writer.Write(rel.Type); + writer.Write(rel.Properties); + writer.Write(rel.ElementId); + } + + writer.Write(indicies); + } + } +} \ No newline at end of file diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs index d9d3b2a5d..c11d5174d 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Internal/IO/ValueSerializers/ElementUnboundRelationshipSerializerTests.cs @@ -32,18 +32,14 @@ public void ShouldDeserialize() var writerMachine = CreateWriterMachine(); var writer = writerMachine.Writer(); - writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); + writer.WriteStructHeader(3, UnboundRelationshipSerializer.UnboundRelationship); writer.Write(1); - writer.Write(2); - writer.Write(3); writer.Write("RELATES_TO"); writer.Write(new Dictionary { {"prop3", true} }); writer.Write("r1"); - writer.Write("n1"); - writer.Write("n2"); var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); @@ -51,8 +47,8 @@ public void ShouldDeserialize() value.Should().NotBeNull(); value.Should().BeOfType().Which.Id.Should().Be(1L); - value.Should().BeOfType().Which.StartNodeId.Should().Be(2L); - value.Should().BeOfType().Which.EndNodeId.Should().Be(3L); + value.Should().BeOfType().Which.StartNodeId.Should().Be(-1L); + value.Should().BeOfType().Which.EndNodeId.Should().Be(-1L); value.Should().BeOfType().Which.Type.Should().Be("RELATES_TO"); value.Should().BeOfType().Which.Properties.Should() .HaveCount(1).And.Contain(new[] @@ -60,8 +56,8 @@ public void ShouldDeserialize() new KeyValuePair("prop3", true), }); value.Should().BeOfType().Which.ElementId.Should().Be("r1"); - value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); - value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("-1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("-1"); } [Fact] @@ -70,9 +66,7 @@ public void ShouldDeserializeWithNulls() var writerMachine = CreateWriterMachine(); var writer = writerMachine.Writer(); - writer.WriteStructHeader(3, ElementRelationshipSerializer.Relationship); - writer.WriteNull(); - writer.WriteNull(); + writer.WriteStructHeader(3, UnboundRelationshipSerializer.UnboundRelationship); writer.WriteNull(); writer.Write("RELATES_TO"); writer.Write(new Dictionary @@ -80,8 +74,6 @@ public void ShouldDeserializeWithNulls() {"prop3", true} }); writer.Write("r1"); - writer.Write("n1"); - writer.Write("n2"); var readerMachine = CreateReaderMachine(writerMachine.GetOutput()); @@ -98,8 +90,8 @@ public void ShouldDeserializeWithNulls() new KeyValuePair("prop3", true), }); value.Should().BeOfType().Which.ElementId.Should().Be("r1"); - value.Should().BeOfType().Which.StartNodeElementId.Should().Be("n1"); - value.Should().BeOfType().Which.EndNodeElementId.Should().Be("n2"); + value.Should().BeOfType().Which.StartNodeElementId.Should().Be("-1"); + value.Should().BeOfType().Which.EndNodeElementId.Should().Be("-1"); } } } \ No newline at end of file From 2ae61180f2a3b8da34cf35a2bd0a4e4c5c4e3bec Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Fri, 4 Mar 2022 15:00:39 +0000 Subject: [PATCH 21/26] add deprecation text --- Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs b/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs index 666bd3fab..501af5f66 100644 --- a/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs +++ b/Neo4j.Driver/Neo4j.Driver/Types/IRelationship.cs @@ -33,13 +33,13 @@ public interface IRelationship : IEntity, IEquatable /// /// Gets the id of the start node of the relationship. /// - [Obsolete] + [Obsolete("Replaced with StartNodeElementId, will be removed in 6.0")] long StartNodeId { get; } /// /// Gets the id of the end node of the relationship. /// - [Obsolete] + [Obsolete("Replaced with EndNodeElementId, will be removed in 6.0")] long EndNodeId { get; } /// From 610676279b94e3d8945a6ea987181aa22f5ab4b5 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 8 Mar 2022 11:17:28 +0000 Subject: [PATCH 22/26] trigger build against handshake testkit --- Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/IO/Connection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/IO/Connection.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/IO/Connection.cs index d61522727..952f7d1e1 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/IO/Connection.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/IO/Connection.cs @@ -88,10 +88,10 @@ public void Close() } public void Dispose() - { + { Dispose(true); GC.SuppressFinalize(this); - } + } protected virtual void Dispose(bool disposing) { From 4334976157f78c7ae24bd92153fcb695290cff37 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Tue, 8 Mar 2022 12:08:30 +0000 Subject: [PATCH 23/26] add elementid feature toggle --- Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs index 08af03626..31ce695ec 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs @@ -15,6 +15,7 @@ static SupportedFeatures() "Detail:ClosedDriverIsEncrypted", "Detail:DefaultSecurityConfigValueEquality", "Feature:API:Driver.IsEncrypted", + "Feature:API:GraphTypes.ElementId", //"Feature:API:Liveness.Check", "Feature:API:Result.List", "Feature:API:Result.Peek", From c03b3e66edc88805b7a28875183b8647b3c5a878 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Wed, 9 Mar 2022 11:31:27 +0000 Subject: [PATCH 24/26] remove feature flag --- Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs index 31ce695ec..08af03626 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/SupportedFeatures.cs @@ -15,7 +15,6 @@ static SupportedFeatures() "Detail:ClosedDriverIsEncrypted", "Detail:DefaultSecurityConfigValueEquality", "Feature:API:Driver.IsEncrypted", - "Feature:API:GraphTypes.ElementId", //"Feature:API:Liveness.Check", "Feature:API:Result.List", "Feature:API:Result.Peek", From 01241c012313da0c0f231cbc75f8f0c3c7000700 Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Fri, 11 Mar 2022 16:58:56 +0000 Subject: [PATCH 25/26] swap spaces, build against new 5.0 --- .../Transaction/TransactionManager.cs | 82 +++++++++---------- 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Transaction/TransactionManager.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Transaction/TransactionManager.cs index 07a8c702b..717581e21 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Transaction/TransactionManager.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Transaction/TransactionManager.cs @@ -6,45 +6,45 @@ namespace Neo4j.Driver.Tests.TestBackend { - internal class TransactionWrapper - { - public IAsyncTransaction Transaction { get; private set; } - private Func> ResultHandler; - - public TransactionWrapper(IAsyncTransaction transaction, Func>resultHandler) - { - Transaction = transaction; - ResultHandler = resultHandler; - } - - public async Task ProcessResults(IResultCursor cursor) - { - return await ResultHandler(cursor); - } - - } - - - internal class TransactionManager - { - private Dictionary Transactions { get; set; } = new Dictionary(); - - public string AddTransaction(TransactionWrapper transation) - { - var key = ProtocolObjectManager.GenerateUniqueIdString(); - Transactions.Add(key, transation); - return key; - } - - public void RemoveTransaction(string key) - { - Transactions.Remove(key); - } - - public TransactionWrapper FindTransaction(string key) - { - return Transactions[key]; - } - - } + internal class TransactionWrapper + { + public IAsyncTransaction Transaction { get; private set; } + private Func> ResultHandler; + + public TransactionWrapper(IAsyncTransaction transaction, Func>resultHandler) + { + Transaction = transaction; + ResultHandler = resultHandler; + } + + public async Task ProcessResults(IResultCursor cursor) + { + return await ResultHandler(cursor); + } + + } + + + internal class TransactionManager + { + private Dictionary Transactions { get; set; } = new Dictionary(); + + public string AddTransaction(TransactionWrapper transation) + { + var key = ProtocolObjectManager.GenerateUniqueIdString(); + Transactions.Add(key, transation); + return key; + } + + public void RemoveTransaction(string key) + { + Transactions.Remove(key); + } + + public TransactionWrapper FindTransaction(string key) + { + return Transactions[key]; + } + + } } From 37911012c25fa2e6942d52d6c31d5308d86f12ab Mon Sep 17 00:00:00 2001 From: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com> Date: Mon, 14 Mar 2022 11:57:48 +0000 Subject: [PATCH 26/26] blacklist summary test while 5.0 server responds 4.4 --- Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs index 69cf09380..63c3a50a1 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/TestBlackList.cs @@ -108,8 +108,9 @@ static class TestBlackList "Backend does not yet support serializing paths"), ("stub.tx_lifetime.test_tx_lifetime.TestTxLifetime.test_managed_tx_raises_tx_managed_exec", - "Driver (still) allows explicit managing of managed transaction") + "Driver (still) allows explicit managing of managed transaction"), + ("test_summary.TestSummary.test_protocol_version_information", "Server not responding with 5.0") }; public static bool FindTest(string testName, out string reason)