Skip to content

Commit 3e9ee16

Browse files
author
Zhen Li
committed
Added the server side packing logic for node, rel, and path back in test util
Some tests needs this server side packer to serialize such types.
1 parent af8c5b7 commit 3e9ee16

File tree

9 files changed

+275
-44
lines changed

9 files changed

+275
-44
lines changed

driver/src/main/java/org/neo4j/driver/internal/messaging/PackStreamMessageFormatV2.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ public MessageFormat.Reader newReader( PackInput input )
8686
return new ReaderV2( input );
8787
}
8888

89-
private static class WriterV2 extends WriterV1
89+
static class WriterV2 extends WriterV1
9090
{
9191
WriterV2( PackOutput output )
9292
{
@@ -226,7 +226,7 @@ private void packPoint3D( Point point ) throws IOException
226226
}
227227
}
228228

229-
private static class ReaderV2 extends ReaderV1
229+
static class ReaderV2 extends ReaderV1
230230
{
231231
ReaderV2( PackInput input )
232232
{

driver/src/test/java/org/neo4j/driver/internal/ValuesTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
import static org.junit.Assert.assertThat;
6969
import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue;
7070
import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue;
71-
import static org.neo4j.driver.internal.util.ValueFactory.pathValue;
71+
import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue;
7272
import static org.neo4j.driver.v1.Values.isoDuration;
7373
import static org.neo4j.driver.v1.Values.ofDouble;
7474
import static org.neo4j.driver.v1.Values.ofFloat;
@@ -549,7 +549,7 @@ public void shouldComplainAboutPathValueType() throws Throwable
549549
exception.expectMessage( "Paths can't be used as parameters." );
550550

551551
// When
552-
PathValue path = pathValue();
552+
PathValue path = filledPathValue();
553553
value( path );
554554
}
555555

@@ -561,7 +561,7 @@ public void shouldComplainAboutPathType() throws Throwable
561561
exception.expectMessage( "Paths can't be used as parameters." );
562562

563563
// When
564-
Path path = pathValue().asPath();
564+
Path path = filledPathValue().asPath();
565565
value( path );
566566
}
567567
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package org.neo4j.driver.internal.messaging;
2+
3+
import java.io.IOException;
4+
import java.util.Map;
5+
6+
import org.neo4j.driver.internal.packstream.PackOutput;
7+
import org.neo4j.driver.internal.types.TypeConstructor;
8+
import org.neo4j.driver.internal.util.Iterables;
9+
import org.neo4j.driver.internal.value.InternalValue;
10+
import org.neo4j.driver.v1.types.Entity;
11+
import org.neo4j.driver.v1.types.Node;
12+
import org.neo4j.driver.v1.types.Path;
13+
import org.neo4j.driver.v1.types.Relationship;
14+
15+
/**
16+
* This class provides the missing server side packing methods to serialize Node, Relationship and Path.
17+
*/
18+
public class KnowledgeablePackStreamMessageFormat extends PackStreamMessageFormatV2
19+
{
20+
@Override
21+
public MessageFormat.Writer newWriter( PackOutput output, boolean byteArraySupportEnabled )
22+
{
23+
return new KnowledgeablePackStreamMessageFormat.Writer( output );
24+
}
25+
26+
private static class Writer extends WriterV2
27+
{
28+
Writer( PackOutput output )
29+
{
30+
super( output );
31+
}
32+
33+
@Override
34+
void packInternalValue( InternalValue value ) throws IOException
35+
{
36+
TypeConstructor typeConstructor = value.typeConstructor();
37+
switch ( typeConstructor )
38+
{
39+
case NODE:
40+
Node node = value.asNode();
41+
packNode( node );
42+
break;
43+
44+
case RELATIONSHIP:
45+
Relationship rel = value.asRelationship();
46+
packRelationship( rel );
47+
break;
48+
49+
case PATH:
50+
Path path = value.asPath();
51+
packPath( path );
52+
break;
53+
default:
54+
super.packInternalValue( value );
55+
}
56+
}
57+
58+
private void packPath( Path path ) throws IOException
59+
{
60+
packer.packStructHeader( 3, PATH );
61+
62+
// Unique nodes
63+
Map<Node,Integer> nodeIdx = Iterables.newLinkedHashMapWithSize( path.length() + 1 );
64+
for ( Node node : path.nodes() )
65+
{
66+
if ( !nodeIdx.containsKey( node ) )
67+
{
68+
nodeIdx.put( node, nodeIdx.size() );
69+
}
70+
}
71+
packer.packListHeader( nodeIdx.size() );
72+
for ( Node node : nodeIdx.keySet() )
73+
{
74+
packNode( node );
75+
}
76+
77+
// Unique rels
78+
Map<Relationship,Integer> relIdx = Iterables.newLinkedHashMapWithSize( path.length() );
79+
for ( Relationship rel : path.relationships() )
80+
{
81+
if ( !relIdx.containsKey( rel ) )
82+
{
83+
relIdx.put( rel, relIdx.size() + 1 );
84+
}
85+
}
86+
packer.packListHeader( relIdx.size() );
87+
for ( Relationship rel : relIdx.keySet() )
88+
{
89+
packer.packStructHeader( 3, UNBOUND_RELATIONSHIP );
90+
packer.pack( rel.id() );
91+
packer.pack( rel.type() );
92+
packProperties( rel );
93+
}
94+
95+
// Sequence
96+
packer.packListHeader( path.length() * 2 );
97+
for ( Path.Segment seg : path )
98+
{
99+
Relationship rel = seg.relationship();
100+
long relEndId = rel.endNodeId();
101+
long segEndId = seg.end().id();
102+
int size = relEndId == segEndId ? relIdx.get( rel ) : -relIdx.get( rel );
103+
packer.pack( size );
104+
packer.pack( nodeIdx.get( seg.end() ) );
105+
}
106+
}
107+
108+
private void packRelationship( Relationship rel ) throws IOException
109+
{
110+
packer.packStructHeader( 5, RELATIONSHIP );
111+
packer.pack( rel.id() );
112+
packer.pack( rel.startNodeId() );
113+
packer.pack( rel.endNodeId() );
114+
115+
packer.pack( rel.type() );
116+
117+
packProperties( rel );
118+
}
119+
120+
private void packNode( Node node ) throws IOException
121+
{
122+
packer.packStructHeader( NODE_FIELDS, NODE );
123+
packer.pack( node.id() );
124+
125+
Iterable<String> labels = node.labels();
126+
packer.packListHeader( Iterables.count( labels ) );
127+
for ( String label : labels )
128+
{
129+
packer.pack( label );
130+
}
131+
132+
packProperties( node );
133+
}
134+
135+
private void packProperties( Entity entity ) throws IOException
136+
{
137+
Iterable<String> keys = entity.keys();
138+
packer.packMapHeader( entity.size() );
139+
for ( String propKey : keys )
140+
{
141+
packer.pack( propKey );
142+
packInternalValue( (InternalValue) entity.get( propKey ) );
143+
}
144+
}
145+
}
146+
}

driver/src/test/java/org/neo4j/driver/internal/messaging/MessageFormatTest.java

Lines changed: 103 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,15 @@
2121
import io.netty.buffer.ByteBuf;
2222
import io.netty.buffer.Unpooled;
2323
import io.netty.channel.embedded.EmbeddedChannel;
24+
import io.netty.handler.codec.EncoderException;
2425
import org.junit.Rule;
2526
import org.junit.Test;
2627
import org.junit.rules.ExpectedException;
2728

28-
import java.util.Collections;
29+
import java.io.IOException;
2930
import java.util.HashMap;
3031
import java.util.List;
3132

32-
import org.neo4j.driver.internal.InternalNode;
33-
import org.neo4j.driver.internal.InternalPath;
34-
import org.neo4j.driver.internal.InternalRelationship;
3533
import org.neo4j.driver.internal.async.BoltProtocolUtil;
3634
import org.neo4j.driver.internal.async.ChannelPipelineBuilderImpl;
3735
import org.neo4j.driver.internal.async.inbound.InboundMessageDispatcher;
@@ -41,13 +39,22 @@
4139
import org.neo4j.driver.v1.exceptions.ClientException;
4240

4341
import static java.util.Arrays.asList;
42+
import static org.hamcrest.CoreMatchers.equalTo;
43+
import static org.hamcrest.CoreMatchers.instanceOf;
44+
import static org.hamcrest.MatcherAssert.assertThat;
4445
import static org.hamcrest.Matchers.startsWith;
4546
import static org.junit.Assert.assertEquals;
4647
import static org.junit.Assert.assertTrue;
48+
import static org.junit.Assert.fail;
4749
import static org.neo4j.driver.internal.async.ChannelAttributes.messageDispatcher;
4850
import static org.neo4j.driver.internal.async.ChannelAttributes.setMessageDispatcher;
4951
import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING;
50-
import static org.neo4j.driver.v1.Values.EmptyMap;
52+
import static org.neo4j.driver.internal.util.ValueFactory.emptyNodeValue;
53+
import static org.neo4j.driver.internal.util.ValueFactory.emptyPathValue;
54+
import static org.neo4j.driver.internal.util.ValueFactory.emptyRelationshipValue;
55+
import static org.neo4j.driver.internal.util.ValueFactory.filledNodeValue;
56+
import static org.neo4j.driver.internal.util.ValueFactory.filledPathValue;
57+
import static org.neo4j.driver.internal.util.ValueFactory.filledRelationshipValue;
5158
import static org.neo4j.driver.v1.Values.ofValue;
5259
import static org.neo4j.driver.v1.Values.parameters;
5360
import static org.neo4j.driver.v1.Values.value;
@@ -80,33 +87,51 @@ public void shouldUnpackAllResponses() throws Throwable
8087
}
8188

8289
@Test
83-
public void shouldUnpackAllValues() throws Throwable
90+
public void shouldPackUnpackValidValues() throws Throwable
8491
{
8592
assertSerializesValue( value( parameters( "cat", null, "dog", null ) ) );
8693
assertSerializesValue( value( parameters( "k", 12, "a", "banana" ) ) );
8794
assertSerializesValue( value( asList( "k", 12, "a", "banana" ) ) );
88-
assertSerializesValue( value(
89-
new InternalNode( 1, Collections.singletonList( "User" ), parameters( "name", "Bob", "age", 45 ).asMap(
90-
ofValue() ) )
91-
) );
92-
assertSerializesValue( value( new InternalNode( 1 ) ) );
93-
assertSerializesValue( value(
94-
new InternalRelationship( 1, 1, 1,
95-
"KNOWS",
96-
parameters( "name", "Bob", "age", 45 ).asMap( ofValue() ) ) ) );
97-
assertSerializesValue( value(
98-
new InternalPath(
99-
new InternalNode( 1 ),
100-
new InternalRelationship( 2, 1, 3,
101-
"KNOWS", EmptyMap.asMap( ofValue() ) ),
102-
new InternalNode( 3 ),
103-
new InternalRelationship( 4, 3, 5,
104-
"LIKES", EmptyMap.asMap( ofValue() ) ),
105-
new InternalNode( 5 )
106-
) ) );
107-
assertSerializesValue( value( new InternalPath( new InternalNode( 1 ) ) ) );
10895
}
10996

97+
@Test
98+
public void shouldUnpackNodeRelationshipAndPath() throws Throwable
99+
{
100+
// Given
101+
assertOnlyDeserializesValue( emptyNodeValue() );
102+
assertOnlyDeserializesValue( filledNodeValue() );
103+
assertOnlyDeserializesValue( emptyRelationshipValue() );
104+
assertOnlyDeserializesValue( filledRelationshipValue() );
105+
assertOnlyDeserializesValue( emptyPathValue() );
106+
assertOnlyDeserializesValue( filledPathValue() );
107+
}
108+
109+
110+
@Test
111+
public void shouldErrorPackingNode() throws Throwable
112+
{
113+
// Given
114+
Value value = filledNodeValue();
115+
expectIOExceptionWithMessage( value, "Unknown type: NODE" );
116+
}
117+
118+
@Test
119+
public void shouldErrorPackingRelationship() throws Throwable
120+
{
121+
// Given
122+
Value value = filledRelationshipValue();
123+
expectIOExceptionWithMessage( value, "Unknown type: RELATIONSHIP" );
124+
}
125+
126+
@Test
127+
public void shouldErrorPackingPath() throws Throwable
128+
{
129+
// Given
130+
Value value = filledPathValue();
131+
expectIOExceptionWithMessage( value, "Unknown type: PATH" );
132+
}
133+
134+
110135
@Test
111136
public void shouldGiveHelpfulErrorOnMalformedNodeStruct() throws Throwable
112137
{
@@ -149,6 +174,11 @@ private void assertSerializes( Message message ) throws Throwable
149174
}
150175

151176
private EmbeddedChannel newEmbeddedChannel()
177+
{
178+
return newEmbeddedChannel( format );
179+
}
180+
181+
private EmbeddedChannel newEmbeddedChannel( MessageFormat format )
152182
{
153183
EmbeddedChannel channel = new EmbeddedChannel();
154184
setMessageDispatcher( channel, new MemorizingInboundMessageDispatcher( channel, DEV_NULL_LOGGING ) );
@@ -186,4 +216,51 @@ private Message unpack( ByteBuf packed, EmbeddedChannel channel ) throws Throwab
186216
assertEquals( 1, unpackedMessages.size() );
187217
return unpackedMessages.get( 0 );
188218
}
219+
220+
private void assertOnlyDeserializesValue( Value value ) throws Throwable
221+
{
222+
RecordMessage message = new RecordMessage( new Value[]{value} );
223+
ByteBuf packed = knowledgeablePack( message );
224+
225+
EmbeddedChannel channel = newEmbeddedChannel();
226+
Message unpackedMessage = unpack( packed, channel );
227+
228+
assertEquals( message, unpackedMessage );
229+
}
230+
231+
private ByteBuf knowledgeablePack( Message message ) throws IOException
232+
{
233+
EmbeddedChannel channel = newEmbeddedChannel( new KnowledgeablePackStreamMessageFormat() );
234+
assertTrue( channel.writeOutbound( message ) );
235+
236+
ByteBuf[] packedMessages = channel.outboundMessages()
237+
.stream()
238+
.map( msg -> (ByteBuf) msg )
239+
.toArray( ByteBuf[]::new );
240+
241+
return Unpooled.wrappedBuffer( packedMessages );
242+
}
243+
244+
private void expectIOExceptionWithMessage( Value value, String errorMessage )
245+
{
246+
RecordMessage message = new RecordMessage( new Value[]{value} );
247+
EmbeddedChannel channel = newEmbeddedChannel();
248+
249+
try
250+
{
251+
pack( message, channel );
252+
fail( "Expecting a EncoderException" );
253+
}
254+
catch ( EncoderException e )
255+
{
256+
Throwable cause = e.getCause();
257+
assertThat( cause, instanceOf( IOException.class ) );
258+
assertThat( cause.getMessage(), equalTo( errorMessage ) );
259+
}
260+
catch ( Exception e )
261+
{
262+
fail( "Expecting a EncoderException but got " + e );
263+
}
264+
}
265+
189266
}

driver/src/test/java/org/neo4j/driver/internal/util/ValueFactory.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,13 @@ public static RelationshipValue filledRelationshipValue()
5454
return new RelationshipValue( new InternalRelationship( 1234, 1, 2, "KNOWS", singletonMap( "name", value( "Dodo" ) ) ) );
5555
}
5656

57-
public static PathValue pathValue()
57+
public static PathValue filledPathValue()
5858
{
5959
return new PathValue( new InternalPath( new InternalNode(42L), new InternalRelationship( 43L, 42L, 44L, "T" ), new InternalNode( 44L ) ) );
6060
}
61+
62+
public static PathValue emptyPathValue()
63+
{
64+
return new PathValue( new InternalPath( new InternalNode( 1 ) ) );
65+
}
6166
}

0 commit comments

Comments
 (0)