diff --git a/driver/clirr-ignored-differences.xml b/driver/clirr-ignored-differences.xml index b9b9547a70..c0944f7808 100644 --- a/driver/clirr-ignored-differences.xml +++ b/driver/clirr-ignored-differences.xml @@ -1,3 +1,22 @@ - + + + + org/neo4j/driver/summary/ServerInfo + 7007 + java.lang.String version() + + + + org/neo4j/driver/summary/ServerInfo + 7012 + java.lang.String protocolVersion() + + + + org/neo4j/driver/summary/ServerInfo + 7012 + java.lang.String agent() + + diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java index dda50e6bda..6bbbd3fd33 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/NetworkConnection.java @@ -54,6 +54,7 @@ public class NetworkConnection implements Connection { private final Channel channel; private final InboundMessageDispatcher messageDispatcher; + private final String serverAgent; private final BoltServerAddress serverAddress; private final ServerVersion serverVersion; private final BoltProtocol protocol; @@ -69,6 +70,7 @@ public NetworkConnection( Channel channel, ExtendedChannelPool channelPool, Cloc { this.channel = channel; this.messageDispatcher = ChannelAttributes.messageDispatcher( channel ); + this.serverAgent = ChannelAttributes.serverAgent( channel ); this.serverAddress = ChannelAttributes.serverAddress( channel ); this.serverVersion = ChannelAttributes.serverVersion( channel ); this.protocol = BoltProtocol.forChannel( channel ); @@ -185,6 +187,12 @@ public void terminateAndRelease( String reason ) } } + @Override + public String serverAgent() + { + return serverAgent; + } + @Override public BoltServerAddress serverAddress() { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java index 8bc781c437..35fddcfc94 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/ChannelAttributes.java @@ -33,6 +33,7 @@ public final class ChannelAttributes private static final AttributeKey CONNECTION_ID = newInstance( "connectionId" ); private static final AttributeKey POOL_ID = newInstance( "poolId" ); private static final AttributeKey PROTOCOL_VERSION = newInstance( "protocolVersion" ); + private static final AttributeKey SERVER_AGENT = newInstance( "serverAgent" ); private static final AttributeKey ADDRESS = newInstance( "serverAddress" ); private static final AttributeKey SERVER_VERSION = newInstance( "serverVersion" ); private static final AttributeKey CREATION_TIMESTAMP = newInstance( "creationTimestamp" ); @@ -74,6 +75,16 @@ public static void setProtocolVersion( Channel channel, BoltProtocolVersion vers setOnce( channel, PROTOCOL_VERSION, version ); } + public static void setServerAgent( Channel channel, String serverAgent ) + { + setOnce( channel, SERVER_AGENT, serverAgent ); + } + + public static String serverAgent( Channel channel ) + { + return get( channel, SERVER_AGENT ); + } + public static BoltServerAddress serverAddress( Channel channel ) { return get( channel, ADDRESS ); diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java index e871a9ac12..1d74213de7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/DirectConnection.java @@ -20,6 +20,7 @@ import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AccessMode; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.DatabaseName; import org.neo4j.driver.internal.DirectConnectionProvider; @@ -28,7 +29,6 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.ServerVersion; -import org.neo4j.driver.AccessMode; /** * This is a connection used by {@link DirectConnectionProvider} to connect to a remote database. @@ -111,6 +111,12 @@ public void terminateAndRelease( String reason ) delegate.terminateAndRelease( reason ); } + @Override + public String serverAgent() + { + return delegate.serverAgent(); + } + @Override public BoltServerAddress serverAddress() { diff --git a/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java index cd2e99c7f3..3799224611 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/async/connection/RoutingConnection.java @@ -20,6 +20,7 @@ import java.util.concurrent.CompletionStage; +import org.neo4j.driver.AccessMode; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.DatabaseName; import org.neo4j.driver.internal.RoutingErrorHandler; @@ -29,7 +30,6 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; import org.neo4j.driver.internal.util.ServerVersion; -import org.neo4j.driver.AccessMode; /** * A connection used by the routing driver. @@ -109,6 +109,12 @@ public void terminateAndRelease( String reason ) delegate.terminateAndRelease( reason ); } + @Override + public String serverAgent() + { + return delegate.serverAgent(); + } + @Override public BoltServerAddress serverAddress() { diff --git a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java index e106cf9fcf..95c97849f7 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java +++ b/driver/src/main/java/org/neo4j/driver/internal/handlers/HelloResponseHandler.java @@ -23,14 +23,16 @@ import java.util.Map; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.messaging.BoltProtocolVersion; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.Value; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAgent; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion; import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion; +import static org.neo4j.driver.internal.util.MetadataExtractor.extractServer; import static org.neo4j.driver.internal.util.ServerVersion.fromBoltProtocolVersion; public class HelloResponseHandler implements ResponseHandler @@ -53,6 +55,9 @@ public void onSuccess( Map metadata ) { try { + Value serverValue = extractServer( metadata ); + setServerAgent( channel, serverValue.asString() ); + // From Server V4 extracting server from metadata in the success message is unreliable // so we fix the Server version against the Bolt Protocol version for Server V4 and above. if ( BoltProtocolV3.VERSION.equals( protocolVersion ) ) diff --git a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java index 26a3c31292..13c2946937 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java +++ b/driver/src/main/java/org/neo4j/driver/internal/spi/Connection.java @@ -51,6 +51,8 @@ public interface Connection void terminateAndRelease( String reason ); + String serverAgent(); + BoltServerAddress serverAddress(); ServerVersion serverVersion(); diff --git a/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java b/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java index 5f6bad23e5..71ab8983b0 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java +++ b/driver/src/main/java/org/neo4j/driver/internal/summary/InternalServerInfo.java @@ -21,18 +21,29 @@ import java.util.Objects; import org.neo4j.driver.internal.BoltServerAddress; +import org.neo4j.driver.internal.messaging.BoltProtocolVersion; import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.summary.ServerInfo; public class InternalServerInfo implements ServerInfo { + private final String agent; private final String address; private final String version; + private final String protocolVersion; - public InternalServerInfo( BoltServerAddress address, ServerVersion version ) + public InternalServerInfo( String agent, BoltServerAddress address, ServerVersion version, BoltProtocolVersion protocolVersion ) { + this.agent = agent; this.address = address.toString(); this.version = version.toString(); + this.protocolVersion = protocolVersion.toString(); + } + + @Override + public String agent() + { + return agent; } @Override @@ -47,6 +58,12 @@ public String version() return version; } + @Override + public String protocolVersion() + { + return protocolVersion; + } + @Override public boolean equals( Object o ) { diff --git a/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java b/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java index 91921f322b..c4968ffc4a 100644 --- a/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java +++ b/driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java @@ -100,7 +100,8 @@ public long extractResultAvailableAfter( Map metadata ) public ResultSummary extractSummary(Query query, Connection connection, long resultAvailableAfter, Map metadata ) { - ServerInfo serverInfo = new InternalServerInfo( connection.serverAddress(), connection.serverVersion() ); + ServerInfo serverInfo = + new InternalServerInfo( connection.serverAgent(), connection.serverAddress(), connection.serverVersion(), connection.protocol().version() ); DatabaseInfo dbInfo = extractDatabaseInfo( metadata ); return new InternalResultSummary(query, serverInfo, dbInfo, extractQueryType( metadata ), extractCounters( metadata ), extractPlan( metadata ), extractProfiledPlan( metadata ), extractNotifications( metadata ), resultAvailableAfter, @@ -132,26 +133,29 @@ public static Bookmark extractBookmarks( Map metadata ) public static ServerVersion extractNeo4jServerVersion( Map metadata ) { - Value versionValue = metadata.get( "server" ); - if ( versionValue == null || versionValue.isNull() ) + Value serverValue = extractServer( metadata ); + ServerVersion server = ServerVersion.version( serverValue.asString() ); + if ( ServerVersion.NEO4J_PRODUCT.equalsIgnoreCase( server.product() ) ) { - throw new UntrustedServerException( "Server provides no product identifier" ); + return server; } else { - ServerVersion server = ServerVersion.version( versionValue.asString() ); - if ( ServerVersion.NEO4J_PRODUCT.equalsIgnoreCase( server.product() ) ) - { - return server; - } - else - { - throw new UntrustedServerException( "Server does not identify as a genuine Neo4j instance: '" + server.product() + "'" ); - } + throw new UntrustedServerException( "Server does not identify as a genuine Neo4j instance: '" + server.product() + "'" ); + } + } + + public static Value extractServer( Map metadata ) + { + Value versionValue = metadata.get( "server" ); + if ( versionValue == null || versionValue.isNull() ) + { + throw new UntrustedServerException( "Server provides no product identifier" ); } + return versionValue; } - private static QueryType extractQueryType(Map metadata ) + private static QueryType extractQueryType( Map metadata ) { Value typeValue = metadata.get( "type" ); if ( typeValue != null ) diff --git a/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java b/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java index fa7ada44a3..5178ebc985 100644 --- a/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java +++ b/driver/src/main/java/org/neo4j/driver/summary/ServerInfo.java @@ -31,9 +31,27 @@ public interface ServerInfo String address(); /** - * Returns a string telling which version of the server the query was executed. - * Supported since neo4j 3.1. + * Returns a string telling which version of the server the query was executed. Supported since neo4j 3.1. + * * @return The server version. + * @deprecated in 4.3, please use {@link ServerInfo#agent()}, {@link ServerInfo#protocolVersion()}, or call the dbms.components procedure instead. + * Method might be removed in the next major release. */ + @Deprecated String version(); + + /** + * Returns Bolt protocol version with which the remote server communicates. This is returned as a string in format X.Y where X is the major version and Y is + * the minor version. + * + * @return The Bolt protocol version. + */ + String protocolVersion(); + + /** + * Returns server agent string by which the remote server identifies itself. + * + * @return The agent string. + */ + String agent(); } diff --git a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java index 75e1793cd5..9fcfec64cf 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/InternalResultTest.java @@ -39,6 +39,7 @@ import org.neo4j.driver.internal.handlers.PullResponseCompletionListener; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.value.NullValue; import org.neo4j.driver.util.Pair; @@ -360,6 +361,8 @@ private Result createResult(int numberOfRecords ) Connection connection = mock( Connection.class ); when( connection.serverAddress() ).thenReturn( LOCAL_DEFAULT ); when( connection.serverVersion() ).thenReturn( anyServerVersion() ); + when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); PullAllResponseHandler pullAllHandler = new LegacyPullAllResponseHandler( query, runHandler, connection, BoltProtocolV3.METADATA_EXTRACTOR, mock( PullResponseCompletionListener.class ) ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java index b7b101f88b..548ac4d4a5 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/AsyncResultCursorImplTest.java @@ -37,6 +37,7 @@ import org.neo4j.driver.internal.handlers.PullAllResponseHandler; import org.neo4j.driver.internal.handlers.RunResponseHandler; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.summary.InternalResultSummary; import org.neo4j.driver.internal.summary.InternalServerInfo; import org.neo4j.driver.internal.summary.InternalSummaryCounters; @@ -86,10 +87,13 @@ void shouldReturnSummary() { PullAllResponseHandler pullAllHandler = mock( PullAllResponseHandler.class ); - ResultSummary summary = new InternalResultSummary( new Query( "RETURN 42" ), - new InternalServerInfo( BoltServerAddress.LOCAL_DEFAULT, anyServerVersion() ), DEFAULT_DATABASE_INFO, QueryType.SCHEMA_WRITE, + ResultSummary summary = new InternalResultSummary( + new Query( "RETURN 42" ), + new InternalServerInfo( "Neo4j/4.2.5", BoltServerAddress.LOCAL_DEFAULT, anyServerVersion(), BoltProtocolV43.VERSION ), + DEFAULT_DATABASE_INFO, QueryType.SCHEMA_WRITE, new InternalSummaryCounters( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 ), - null, null, emptyList(), 42, 42 ); + null, null, emptyList(), 42, 42 + ); when( pullAllHandler.consumeAsync() ).thenReturn( completedFuture( summary ) ); AsyncResultCursorImpl cursor = newCursor( pullAllHandler ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java index 8fc4b63d25..d118fb6dcf 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/NetworkConnectionTest.java @@ -348,6 +348,31 @@ void shouldNotWriteAndFlushMultipleMessagesWhenTerminated() assertConnectionTerminatedError( failureCaptor.getValue() ); } + @Test + void shouldReturnServerAgentWhenCreated() + { + EmbeddedChannel channel = newChannel(); + String agent = "Neo4j/4.2.5"; + ChannelAttributes.setServerAgent( channel, agent ); + + NetworkConnection connection = newConnection( channel ); + + assertEquals( agent, connection.serverAgent() ); + } + + @Test + void shouldReturnServerAgentWhenReleased() + { + EmbeddedChannel channel = newChannel(); + String agent = "Neo4j/4.2.5"; + ChannelAttributes.setServerAgent( channel, agent ); + + NetworkConnection connection = newConnection( channel ); + connection.release(); + + assertEquals( agent, connection.serverAgent() ); + } + @Test void shouldReturnServerAddressWhenReleased() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java index f20e85e8b7..d63ad4d8cc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/ChannelAttributesTest.java @@ -36,6 +36,7 @@ import static org.neo4j.driver.internal.async.connection.ChannelAttributes.messageDispatcher; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.protocolVersion; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAgent; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setConnectionId; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setCreationTimestamp; @@ -43,6 +44,7 @@ import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setProtocolVersion; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAddress; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerAgent; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setServerVersion; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setTerminationReason; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.terminationReason; @@ -82,6 +84,23 @@ void shouldFailToSetProtocolVersionTwice() assertThrows( IllegalStateException.class, () -> setProtocolVersion( channel, new BoltProtocolVersion( 43, 0 ) ) ); } + @Test + void shouldSetAndGetServerAgent() + { + String agent = "Neo4j/4.2.5"; + setServerAgent( channel, agent ); + assertEquals( agent, serverAgent( channel ) ); + } + + @Test + void shouldFailToSetServerAgentTwice() + { + String agent = "Neo4j/4.2.5"; + setServerAgent( channel, agent ); + + assertThrows( IllegalStateException.class, () -> setServerAgent( channel, agent ) ); + } + @Test void shouldSetAndGetAddress() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java new file mode 100644 index 0000000000..3e843dad75 --- /dev/null +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/DirectConnectionTest.java @@ -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. + */ +package org.neo4j.driver.internal.async.connection; + +import org.junit.jupiter.api.Test; + +import org.neo4j.driver.internal.spi.Connection; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; +import static org.mockito.Mockito.mock; +import static org.neo4j.driver.AccessMode.READ; +import static org.neo4j.driver.internal.DatabaseNameUtil.defaultDatabase; + +public class DirectConnectionTest +{ + @Test + void shouldReturnServerAgent() + { + // given + Connection connection = mock( Connection.class ); + DirectConnection directConnection = new DirectConnection( connection, defaultDatabase(), READ ); + String agent = "Neo4j/4.2.5"; + given( connection.serverAgent() ).willReturn( agent ); + + // when + String actualAgent = directConnection.serverAgent(); + + // then + assertEquals( agent, actualAgent ); + then( connection ).should().serverAgent(); + } +} diff --git a/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java index 2d0727ada8..aac0b28b32 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/async/connection/RoutingConnectionTest.java @@ -28,6 +28,9 @@ import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.junit.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.then; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -62,6 +65,24 @@ void shouldWrapHandlersWhenWritingAndFlushingMultipleMessages() testHandlersWrappingWithMultipleMessages( true ); } + @Test + void shouldReturnServerAgent() + { + // given + Connection connection = mock( Connection.class ); + RoutingErrorHandler errorHandler = mock( RoutingErrorHandler.class ); + RoutingConnection routingConnection = new RoutingConnection( connection, defaultDatabase(), READ, errorHandler ); + String agent = "Neo4j/4.2.5"; + given( connection.serverAgent() ).willReturn( agent ); + + // when + String actualAgent = routingConnection.serverAgent(); + + // then + assertEquals( agent, actualAgent ); + then( connection ).should().serverAgent(); + } + private static void testHandlersWrappingWithSingleMessage( boolean flush ) { Connection connection = mock( Connection.class ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java index bbc749449c..2fd3600b6e 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/HelloResponseHandlerTest.java @@ -48,6 +48,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.neo4j.driver.Values.value; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.connectionId; +import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverAgent; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.serverVersion; import static org.neo4j.driver.internal.async.connection.ChannelAttributes.setMessageDispatcher; import static org.neo4j.driver.internal.async.outbound.OutboundMessageHandler.NAME; @@ -86,6 +87,20 @@ void shouldSetServerVersionOnChannel() assertEquals( anyServerVersion(), serverVersion( channel ) ); } + @Test + void shouldSetServerAgentOnChannel() + { + ChannelPromise channelPromise = channel.newPromise(); + HelloResponseHandler handler = new HelloResponseHandler( channelPromise, BoltProtocolV3.VERSION ); + + String agent = "Neo4j/4.2.5"; + Map metadata = metadata( agent, "bolt-1" ); + handler.onSuccess( metadata ); + + assertTrue( channelPromise.isSuccess() ); + assertEquals( agent, serverAgent( channel ) ); + } + @Test void shouldThrowWhenServerVersionNotReturned() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java index a23f543eec..78763751d0 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/PullAllResponseHandlerTestBase.java @@ -22,9 +22,7 @@ import java.io.IOException; import java.nio.channels.ClosedChannelException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.function.Function; @@ -36,10 +34,10 @@ import org.neo4j.driver.exceptions.SessionExpiredException; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.InternalRecord; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; -import org.neo4j.driver.internal.value.BooleanValue; -import org.neo4j.driver.summary.ResultSummary; import org.neo4j.driver.summary.QueryType; +import org.neo4j.driver.summary.ResultSummary; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -714,6 +712,8 @@ protected Connection connectionMock() Connection connection = mock( Connection.class ); when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT ); when( connection.serverVersion() ).thenReturn( anyServerVersion() ); + when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); return connection; } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java index f97117418e..7f3bf6e8bc 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/SessionPullResponseCompletionListenerTest.java @@ -28,6 +28,7 @@ import org.neo4j.driver.internal.InternalBookmark; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; import org.neo4j.driver.internal.messaging.v3.BoltProtocolV3; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ResponseHandler; @@ -94,6 +95,8 @@ private static Connection newConnectionMock() Connection connection = mock( Connection.class ); when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT ); when( connection.serverVersion() ).thenReturn( anyServerVersion() ); + when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); return connection; } } diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java index 6575c0d723..8ef5b5a6c6 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/TransactionPullResponseCompletionListenerTest.java @@ -32,6 +32,7 @@ import org.neo4j.driver.internal.async.UnmanagedTransaction; import org.neo4j.driver.internal.handlers.pulln.BasicPullResponseHandler; import org.neo4j.driver.internal.handlers.pulln.PullResponseHandler; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; import static org.mockito.Mockito.mock; @@ -63,13 +64,19 @@ private static void testErrorHandling( Throwable error ) Connection connection = mock( Connection.class ); when( connection.serverAddress() ).thenReturn( BoltServerAddress.LOCAL_DEFAULT ); when( connection.serverVersion() ).thenReturn( anyServerVersion() ); + when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); UnmanagedTransaction tx = mock( UnmanagedTransaction.class ); TransactionPullResponseCompletionListener listener = new TransactionPullResponseCompletionListener( tx ); RunResponseHandler runHandler = new RunResponseHandler( new CompletableFuture<>(), METADATA_EXTRACTOR ); PullResponseHandler handler = new BasicPullResponseHandler( new Query( "RETURN 1" ), runHandler, - connection, METADATA_EXTRACTOR, listener ); - handler.installRecordConsumer( ( record, throwable ) -> {} ); - handler.installSummaryConsumer( ( resultSummary, throwable ) -> {} ); + connection, METADATA_EXTRACTOR, listener ); + handler.installRecordConsumer( ( record, throwable ) -> + { + } ); + handler.installSummaryConsumer( ( resultSummary, throwable ) -> + { + } ); handler.onFailure( error ); diff --git a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java index 09349ad2b1..eb8f68e699 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java +++ b/driver/src/test/java/org/neo4j/driver/internal/handlers/pulln/BasicPullResponseHandlerTestBase.java @@ -26,14 +26,15 @@ import java.util.function.BiConsumer; import java.util.stream.Stream; +import org.neo4j.driver.Record; +import org.neo4j.driver.Value; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.messaging.request.DiscardMessage; import org.neo4j.driver.internal.messaging.request.PullMessage; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.util.ServerVersion; import org.neo4j.driver.internal.value.BooleanValue; -import org.neo4j.driver.Record; -import org.neo4j.driver.Value; import org.neo4j.driver.summary.ResultSummary; import static org.hamcrest.CoreMatchers.equalTo; @@ -241,6 +242,8 @@ static Connection mockConnection() Connection conn = mock( Connection.class ); when( conn.serverAddress() ).thenReturn( mock( BoltServerAddress.class ) ); when( conn.serverVersion() ).thenReturn( mock( ServerVersion.class ) ); + when( conn.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( conn.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); return conn; } diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java index a7441da5e8..57bb9b3c5b 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/FailingConnectionDriverFactory.java @@ -25,6 +25,8 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.Config; import org.neo4j.driver.internal.BoltServerAddress; import org.neo4j.driver.internal.DriverFactory; import org.neo4j.driver.internal.cluster.RoutingContext; @@ -35,8 +37,6 @@ import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.spi.ConnectionPool; import org.neo4j.driver.internal.spi.ResponseHandler; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.Config; public class FailingConnectionDriverFactory extends DriverFactory { @@ -194,6 +194,12 @@ public void terminateAndRelease( String reason ) delegate.terminateAndRelease( reason ); } + @Override + public String serverAgent() + { + return delegate.serverAgent(); + } + @Override public BoltServerAddress serverAddress() { diff --git a/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java b/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java index e228b36cb0..afbc7c5dd2 100644 --- a/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java +++ b/driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java @@ -25,14 +25,15 @@ import java.util.Map; import java.util.concurrent.TimeUnit; +import org.neo4j.driver.Bookmark; import org.neo4j.driver.Query; import org.neo4j.driver.Value; import org.neo4j.driver.Values; import org.neo4j.driver.exceptions.UntrustedServerException; import org.neo4j.driver.exceptions.value.Uncoercible; import org.neo4j.driver.internal.BoltServerAddress; -import org.neo4j.driver.Bookmark; import org.neo4j.driver.internal.InternalBookmark; +import org.neo4j.driver.internal.messaging.v43.BoltProtocolV43; import org.neo4j.driver.internal.spi.Connection; import org.neo4j.driver.internal.summary.InternalInputPosition; import org.neo4j.driver.summary.DatabaseInfo; @@ -60,6 +61,7 @@ import static org.neo4j.driver.internal.summary.InternalSummaryCounters.EMPTY_STATS; import static org.neo4j.driver.internal.util.MetadataExtractor.extractDatabaseInfo; import static org.neo4j.driver.internal.util.MetadataExtractor.extractNeo4jServerVersion; +import static org.neo4j.driver.internal.util.MetadataExtractor.extractServer; import static org.neo4j.driver.internal.util.ServerVersion.v4_0_0; import static org.neo4j.driver.summary.QueryType.READ_ONLY; import static org.neo4j.driver.summary.QueryType.READ_WRITE; @@ -78,7 +80,7 @@ class MetadataExtractorTest void shouldExtractQueryKeys() { List keys = asList( "hello", " ", "world", "!" ); - Map keyIndex = new HashMap<>(); + Map keyIndex = new HashMap<>(); keyIndex.put( "hello", 0 ); keyIndex.put( " ", 1 ); keyIndex.put( "world", 2 ); @@ -116,11 +118,11 @@ void shouldExtractNoResultAvailableAfterWhenNoneInMetadata() void shouldBuildResultSummaryWithQuery() { Query query = new Query( "UNWIND range(10, 100) AS x CREATE (:Node {name: $name, x: x})", - singletonMap( "name", "Apa" ) ); + singletonMap( "name", "Apa" ) ); - ResultSummary summary = extractor.extractSummary(query, connectionMock(), 42, emptyMap() ); + ResultSummary summary = extractor.extractSummary( query, connectionMock(), 42, emptyMap() ); - assertEquals(query, summary.query() ); + assertEquals( query, summary.query() ); } @Test @@ -413,6 +415,16 @@ void shouldExtractServerVersion() assertEquals( ServerVersion.v3_5_0, version ); } + @Test + void shouldExtractServer() + { + String agent = "Neo4j/3.5.0"; + Map metadata = singletonMap( "server", value( agent ) ); + + Value serverValue = extractServer( metadata ); + + assertEquals( agent, serverValue.asString() ); + } @Test void shouldExtractDatabase() @@ -466,7 +478,7 @@ void shouldFailToExtractServerVersionFromNonNeo4jProduct() assertThrows( UntrustedServerException.class, () -> extractNeo4jServerVersion( singletonMap( "server", value( "NotNeo4j/1.2.3" ) ) ) ); } - private ResultSummary createWithQueryType(Value typeValue ) + private ResultSummary createWithQueryType( Value typeValue ) { Map metadata = singletonMap( "type", typeValue ); return extractor.extractSummary( query(), connectionMock(), 42, metadata ); @@ -487,6 +499,8 @@ private static Connection connectionMock( BoltServerAddress address, ServerVersi Connection connection = mock( Connection.class ); when( connection.serverAddress() ).thenReturn( address ); when( connection.serverVersion() ).thenReturn( version ); + when( connection.protocol() ).thenReturn( BoltProtocolV43.INSTANCE ); + when( connection.serverAgent() ).thenReturn( "Neo4j/4.2.5" ); return connection; } } diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java index 3a4df65624..6722ebd5ab 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultConsume.java @@ -23,7 +23,7 @@ import lombok.Setter; import neo4j.org.testkit.backend.TestkitState; import neo4j.org.testkit.backend.messages.responses.NullRecord; -import neo4j.org.testkit.backend.messages.responses.ResultSummary; +import neo4j.org.testkit.backend.messages.responses.Summary; import neo4j.org.testkit.backend.messages.responses.TestkitResponse; import org.neo4j.driver.Result; @@ -42,8 +42,17 @@ public TestkitResponse process( TestkitState testkitState ) try { Result result = testkitState.getResults().get( data.getResultId() ); - result.consume(); - return ResultSummary.builder().build(); + org.neo4j.driver.summary.ResultSummary summary = result.consume(); + Summary.ServerInfo serverInfo = Summary.ServerInfo.builder() + .protocolVersion( summary.server().protocolVersion() ) + .agent( summary.server().agent() ) + .build(); + Summary.SummaryBody data = Summary.SummaryBody.builder() + .serverInfo( serverInfo ) + .build(); + return Summary.builder() + .data( data ) + .build(); } catch ( NoSuchRecordException ignored ) { diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java similarity index 69% rename from testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java index 0df7a42e1a..db32033abb 100644 --- a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/ResultSummary.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Summary.java @@ -25,11 +25,31 @@ @Setter @Getter @Builder -public class ResultSummary implements TestkitResponse +public class Summary implements TestkitResponse { + private SummaryBody data; + @Override public String testkitName() { - return "ResultSummary"; + return "Summary"; + } + + @Setter + @Getter + @Builder + public static class SummaryBody + { + private ServerInfo serverInfo; + } + + @Setter + @Getter + @Builder + public static class ServerInfo + { + private String protocolVersion; + + private String agent; } }