From 4579f14de5a0a29698744d6584fab16cfc5e9c00 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 4 Apr 2023 16:26:31 +0200 Subject: [PATCH 01/18] Enables Deno specific tests in Core Core features which are environment dependent will need to be implemented. For testing this features, environment dependent tests need to be setup. The `deno` tests can be run by `npm run test::deno` and they need to be located under `./packages/core/test/deno`. The testkit pipeline is configured to run these tests only when configured for test `deno`. --- package.json | 1 + packages/core/jest.config.ts | 7 ++++--- packages/core/package.json | 1 + packages/core/test/deno/.gitkeep | 0 testkit/unittests.py | 3 +++ 5 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 packages/core/test/deno/.gitkeep diff --git a/package.json b/package.json index f34bc042f..d97175a53 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "build::notci": "lerna bootstrap", "docs": "lerna run docs --stream --concurrency 1", "test::unit": "lerna run test::unit --stream", + "test::deno": "lerna run test::deno --stream", "test::integration": "lerna run test::integration --stream", "test::browser": "lerna run test::browser --stream", "test::stress": "lerna run test::stress --stream", diff --git a/packages/core/jest.config.ts b/packages/core/jest.config.ts index 538508c09..b717b2106 100644 --- a/packages/core/jest.config.ts +++ b/packages/core/jest.config.ts @@ -152,9 +152,10 @@ export default { // ], // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped - // testPathIgnorePatterns: [ - // "/node_modules/" - // ], + testPathIgnorePatterns: [ + "/node_modules/", + "/test/deno/" + ], // The regexp pattern or array of patterns that Jest uses to detect test files // testRegex: [], diff --git a/packages/core/package.json b/packages/core/package.json index 67bc6a123..175b0198e 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,6 +10,7 @@ "test": "jest", "test::watch": "jest --watch", "test::unit": "npm run test", + "test::deno": "deno test --allow-none ./test/deno/", "predocs": "npm run build && npm run build::es6", "docs": "esdoc -c esdoc.json", "prepare": "npm run build", diff --git a/packages/core/test/deno/.gitkeep b/packages/core/test/deno/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/testkit/unittests.py b/testkit/unittests.py index 791590a7c..9ae1753a9 100644 --- a/testkit/unittests.py +++ b/testkit/unittests.py @@ -14,3 +14,6 @@ run_in_driver_repo(["npm", "run", "lint"]) run_in_driver_repo(["npm", "run", "test::unit", "--", ignore]) + + if is_deno(): + run_in_driver_repo(["npm" "run", "test::deno"]) From 4cbb9133602fb2c625a3aa1fdab94774ad5eacdf Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Wed, 5 Apr 2023 15:20:16 +0100 Subject: [PATCH 02/18] Bolt agent added to hello metadata --- .../src/bolt/bolt-protocol-v1.js | 4 +- .../src/bolt/bolt-protocol-v3.js | 4 +- .../src/bolt/bolt-protocol-v4x1.js | 4 +- .../src/bolt/bolt-protocol-v4x3.js | 4 +- .../src/bolt/bolt-protocol-v5x0.js | 4 +- .../src/bolt/bolt-protocol-v5x1.js | 4 +- .../src/bolt/bolt-protocol-v5x2.js | 6 +- .../src/bolt/bolt-protocol-v5x3.js | 78 ++ .../bolt/bolt-protocol-v5x3.transformer.js | 24 + packages/bolt-connection/src/bolt/create.js | 9 + .../bolt-connection/src/bolt/handshake.js | 2 +- .../src/bolt/request-message.js | 32 + .../authentication-provider.js | 5 +- .../connection-provider-direct.js | 4 +- .../connection-provider-pooled.js | 8 +- .../connection-provider-routing.js | 3 +- .../src/connection/connection-channel.js | 9 +- .../src/connection/connection-delegate.js | 4 +- .../src/connection/connection.js | 3 +- .../test/bolt/bolt-protocol-v5x3.test.js | 1104 +++++++++++++++++ .../bolt-connection/test/bolt/index.test.js | 8 +- packages/core/package.json | 3 + packages/core/src/connection.ts | 4 +- .../internal/bolt-agent/browser/BoltAgent.ts | 21 + .../src/internal/bolt-agent/browser/index.ts | 20 + .../src/internal/bolt-agent/deno/BoltAgent.ts | 21 + .../src/internal/bolt-agent/deno/index.ts | 20 + .../core/src/internal/bolt-agent/index.ts | 20 + .../src/internal/bolt-agent/node/BoltAgent.ts | 44 + .../src/internal/bolt-agent/node/index.ts | 20 + packages/core/src/internal/constants.ts | 4 +- packages/core/src/internal/index.ts | 4 +- packages/core/src/types.ts | 1 + .../bolt-agent/node/bolt-agent.test.ts | 43 + packages/neo4j-driver-deno/generate.ts | 10 +- .../authentication-provider.js | 5 +- .../connection-provider-pooled.js | 5 +- .../connection/connection-channel.js | 9 +- packages/neo4j-driver-deno/lib/core/types.ts | 3 +- packages/neo4j-driver-lite/src/index.ts | 9 +- packages/neo4j-driver/src/index.js | 9 +- .../testkit-backend/src/feature/common.js | 1 + 42 files changed, 1545 insertions(+), 54 deletions(-) create mode 100644 packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js create mode 100644 packages/bolt-connection/src/bolt/bolt-protocol-v5x3.transformer.js create mode 100644 packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js create mode 100644 packages/core/src/internal/bolt-agent/browser/BoltAgent.ts create mode 100644 packages/core/src/internal/bolt-agent/browser/index.ts create mode 100644 packages/core/src/internal/bolt-agent/deno/BoltAgent.ts create mode 100644 packages/core/src/internal/bolt-agent/deno/index.ts create mode 100644 packages/core/src/internal/bolt-agent/index.ts create mode 100644 packages/core/src/internal/bolt-agent/node/BoltAgent.ts create mode 100644 packages/core/src/internal/bolt-agent/node/index.ts create mode 100644 packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index 87b736ce7..b7be12d3c 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -175,7 +175,7 @@ export default class BoltProtocol { * @param {function()} param.onComplete the callback to invoke on completion. * @returns {StreamObserver} the stream observer that monitors the corresponding server response. */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) @@ -184,7 +184,7 @@ export default class BoltProtocol { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.init(userAgent, authToken), observer, true) + this.write(RequestMessage.init(userAgent || boltAgent, authToken), observer, true) return observer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js index 783e35080..2b0b095fc 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -69,7 +69,7 @@ export default class BoltProtocol extends BoltProtocolV2 { return metadata } - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -78,7 +78,7 @@ export default class BoltProtocol extends BoltProtocolV2 { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.hello(userAgent, authToken), observer, true) + this.write(RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) return observer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js index d507241d2..8c16a1637 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js @@ -73,7 +73,7 @@ export default class BoltProtocol extends BoltProtocolV4 { return this._transformer } - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -83,7 +83,7 @@ export default class BoltProtocol extends BoltProtocolV4 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js index 291c348a8..6252a1838 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js @@ -89,7 +89,7 @@ export default class BoltProtocol extends BoltProtocolV42 { * @param {function(onComplte)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => { @@ -104,7 +104,7 @@ export default class BoltProtocol extends BoltProtocolV42 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting, ['utc']), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js index 85000baa5..9ec0c4cac 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js @@ -53,7 +53,7 @@ export default class BoltProtocol extends BoltProtocolV44 { * @param {function(onComplte)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -63,7 +63,7 @@ export default class BoltProtocol extends BoltProtocolV44 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index 7fef848d4..04e342342 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -57,7 +57,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { * @param {function(onComplete)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const state = {} const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), @@ -71,7 +71,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello5x1(userAgent, this._serversideRouting), + RequestMessage.hello5x1(userAgent === '' || userAgent == null ? boltAgent : userAgent, this._serversideRouting), observer, false ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js index 4d49b5b42..5e63110a6 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js @@ -50,13 +50,14 @@ export default class BoltProtocol extends BoltProtocolV5x1 { * * @param {Object} args The params * @param {string} args.userAgent The user agent + * @param {string} args.boltAgent The bolt agent * @param {any} args.authToken The auth token * @param {NotificationFilter} args.notificationFilter The notification filters. * @param {function(error)} args.onError On error callback * @param {function(onComplete)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const state = {} const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), @@ -67,7 +68,8 @@ export default class BoltProtocol extends BoltProtocolV5x1 { }) this.write( - RequestMessage.hello5x2(userAgent, notificationFilter, this._serversideRouting), + // if useragent is null then for all versions before 5.3 it should be bolt agent by default + RequestMessage.hello5x2(userAgent === '' || userAgent == null ? boltAgent : userAgent, notificationFilter, this._serversideRouting), observer, false ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js new file mode 100644 index 000000000..1b085989f --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.js @@ -0,0 +1,78 @@ +/** + * 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. + */ +import BoltProtocolV5x2 from './bolt-protocol-v5x2' + +import transformersFactories from './bolt-protocol-v5x3.transformer' +import Transformer from './transformer' +import RequestMessage from './request-message' +import { LoginObserver } from './stream-observers' + +import { internal } from 'neo4j-driver-core' + +const { + constants: { BOLT_PROTOCOL_V5_3 } +} = internal + +export default class BoltProtocol extends BoltProtocolV5x2 { + get version () { + return BOLT_PROTOCOL_V5_3 + } + + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + } + return this._transformer + } + + /** + * Initialize a connection with the server + * + * @param {Object} args The params + * @param {string} args.userAgent The user agent + * @param {any} args.authToken The auth token + * @param {NotificationFilter} args.notificationFilter The notification filters. + * @param {function(error)} args.onError On error callback + * @param {function(onComplete)} args.onComplete On complete callback + * @returns {LoginObserver} The Login observer + */ + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { + const state = {} + const observer = new LoginObserver({ + onError: error => this._onLoginError(error, onError), + onCompleted: metadata => { + state.metadata = metadata + return this._onLoginCompleted(metadata) + } + }) + + this.write( + RequestMessage.hello5x3(userAgent, boltAgent, notificationFilter, this._serversideRouting), + observer, + false + ) + + return this.logon({ + authToken, + onComplete: metadata => onComplete({ ...metadata, ...state.metadata }), + onError, + flush: true + }) + } +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.transformer.js new file mode 100644 index 000000000..12e1c7451 --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x3.transformer.js @@ -0,0 +1,24 @@ +/** + * 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. + */ + +import v5x2 from './bolt-protocol-v5x2.transformer' + +export default { + ...v5x2 +} diff --git a/packages/bolt-connection/src/bolt/create.js b/packages/bolt-connection/src/bolt/create.js index 21539cb54..92906fda9 100644 --- a/packages/bolt-connection/src/bolt/create.js +++ b/packages/bolt-connection/src/bolt/create.js @@ -29,6 +29,7 @@ import BoltProtocolV4x4 from './bolt-protocol-v4x4' import BoltProtocolV5x0 from './bolt-protocol-v5x0' import BoltProtocolV5x1 from './bolt-protocol-v5x1' import BoltProtocolV5x2 from './bolt-protocol-v5x2' +import BoltProtocolV5x3 from './bolt-protocol-v5x3' // eslint-disable-next-line no-unused-vars import { Chunker, Dechunker } from '../channel' import ResponseHandler from './response-handler' @@ -213,6 +214,14 @@ function createProtocol ( onProtocolError, serversideRouting ) + case 5.3: + return new BoltProtocolV5x3(server, + chunker, + packingConfig, + createResponseHandler, + log, + onProtocolError, + serversideRouting) default: throw newError('Unknown Bolt protocol version: ' + version) } diff --git a/packages/bolt-connection/src/bolt/handshake.js b/packages/bolt-connection/src/bolt/handshake.js index 76067aba4..5ea0ed0ba 100644 --- a/packages/bolt-connection/src/bolt/handshake.js +++ b/packages/bolt-connection/src/bolt/handshake.js @@ -76,7 +76,7 @@ function parseNegotiatedResponse (buffer) { */ function newHandshakeBuffer () { return createHandshakeMessage([ - [version(5, 2), version(5, 0)], + [version(5, 3), version(5, 0)], [version(4, 4), version(4, 2)], version(4, 1), version(3, 0) diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index b60486962..ddcc4cfdc 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -191,6 +191,38 @@ export default class RequestMessage { ) } + /** + * Create a new HELLO message. + * @param {string} userAgent the user agent. + * @param {string} boltAgent the bolt agent. + * @param {NotificationFilter} notificationFilter the notification filter configured + * @param {Object} routing server side routing, set to routing context to turn on server side routing (> 4.1) + * @return {RequestMessage} new HELLO message. + */ + static hello5x3 (userAgent, boltAgent, notificationFilter = null, routing = null) { + const metadata = { user_agent: userAgent, bolt_agent: boltAgent } + + if (notificationFilter) { + if (notificationFilter.minimumSeverityLevel) { + metadata.notifications_minimum_severity = notificationFilter.minimumSeverityLevel + } + + if (notificationFilter.disabledCategories) { + metadata.notifications_disabled_categories = notificationFilter.disabledCategories + } + } + + if (routing) { + metadata.routing = routing + } + + return new RequestMessage( + HELLO, + [metadata], + () => `HELLO ${json.stringify(metadata)}` + ) + } + /** * Create a new LOGON message. * diff --git a/packages/bolt-connection/src/connection-provider/authentication-provider.js b/packages/bolt-connection/src/connection-provider/authentication-provider.js index 7f406d127..c69d21cff 100644 --- a/packages/bolt-connection/src/connection-provider/authentication-provider.js +++ b/packages/bolt-connection/src/connection-provider/authentication-provider.js @@ -24,11 +24,12 @@ import { object } from '../lang' * Class which provides Authorization for {@link Connection} */ export default class AuthenticationProvider { - constructor ({ authTokenManager, userAgent }) { + constructor ({ authTokenManager, userAgent, boltAgent }) { this._authTokenManager = authTokenManager || expirationBasedAuthTokenManager({ tokenProvider: () => {} }) this._userAgent = userAgent + this._boltAgent = boltAgent } async authenticate ({ connection, auth, skipReAuth, waitReAuth, forceReAuth }) { @@ -38,7 +39,7 @@ export default class AuthenticationProvider { forceReAuth === true ) if (connection.authToken == null || shouldReAuth) { - return await connection.connect(this._userAgent, auth, waitReAuth || false) + return await connection.connect(this._userAgent, this._boltAgent, auth, waitReAuth || false) } return connection } diff --git a/packages/bolt-connection/src/connection-provider/connection-provider-direct.js b/packages/bolt-connection/src/connection-provider/connection-provider-direct.js index fd5ed21bd..0f2b5ae8a 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-direct.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-direct.js @@ -37,8 +37,8 @@ const { const { SERVICE_UNAVAILABLE } = error export default class DirectConnectionProvider extends PooledConnectionProvider { - constructor ({ id, config, log, address, userAgent, authTokenManager, newPool }) { - super({ id, config, log, userAgent, authTokenManager, newPool }) + constructor ({ id, config, log, address, userAgent, boltAgent, authTokenManager, newPool }) { + super({ id, config, log, userAgent, boltAgent, authTokenManager, newPool }) this._address = address } diff --git a/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js b/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js index 2c297daa9..f7bf6577b 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js @@ -33,7 +33,7 @@ const AUTHENTICATION_ERRORS = [ export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, + { id, config, log, userAgent, boltAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, createChannelConnectionHook = null ) { super() @@ -42,7 +42,9 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._config = config this._log = log this._authTokenManager = authTokenManager - this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent }) + this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent, boltAgent }) + this._userAgent = userAgent + this._boltAgent = boltAgent this._createChannelConnection = createChannelConnectionHook || (address => { @@ -67,7 +69,6 @@ export default class PooledConnectionProvider extends ConnectionProvider { config: PoolConfig.fromDriverConfig(config), log: this._log }) - this._userAgent = userAgent this._openConnections = {} } @@ -86,6 +87,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { return release(address, connection) } this._openConnections[connection.id] = connection + return this._authenticationProvider.authenticate({ connection, auth }) .catch(error => { // let's destroy this connection diff --git a/packages/bolt-connection/src/connection-provider/connection-provider-routing.js b/packages/bolt-connection/src/connection-provider/connection-provider-routing.js index 65e077100..b973269fc 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-routing.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-routing.js @@ -67,11 +67,12 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider config, log, userAgent, + boltAgent, authTokenManager, routingTablePurgeDelay, newPool }) { - super({ id, config, log, userAgent, authTokenManager, newPool }, address => { + super({ id, config, log, userAgent, boltAgent, authTokenManager, newPool }, address => { return createChannelConnection( address, this._config, diff --git a/packages/bolt-connection/src/connection/connection-channel.js b/packages/bolt-connection/src/connection/connection-channel.js index 38bcd2fa5..53f1ea668 100644 --- a/packages/bolt-connection/src/connection/connection-channel.js +++ b/packages/bolt-connection/src/connection/connection-channel.js @@ -183,10 +183,11 @@ export default class ChannelConnection extends Connection { /** * Send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - async connect (userAgent, authToken, waitReAuth) { + async connect (userAgent, boltAgent, authToken, waitReAuth) { if (this._protocol.initialized && !this._protocol.supportsReAuth) { throw newError('Connection does not support re-auth') } @@ -194,7 +195,7 @@ export default class ChannelConnection extends Connection { this._authToken = authToken if (!this._protocol.initialized) { - return await this._initialize(userAgent, authToken) + return await this._initialize(userAgent, boltAgent, authToken) } if (waitReAuth) { @@ -221,14 +222,16 @@ export default class ChannelConnection extends Connection { /** * Perform protocol-specific initialization which includes authentication. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if initialization is successful. Rejected promise otherwise. */ - _initialize (userAgent, authToken) { + _initialize (userAgent, boltAgent, authToken) { const self = this return new Promise((resolve, reject) => { this._protocol.initialize({ userAgent, + boltAgent, authToken, notificationFilter: this._notificationFilter, onError: err => reject(err), diff --git a/packages/bolt-connection/src/connection/connection-delegate.js b/packages/bolt-connection/src/connection/connection-delegate.js index 1f77823f7..8848408d3 100644 --- a/packages/bolt-connection/src/connection/connection-delegate.js +++ b/packages/bolt-connection/src/connection/connection-delegate.js @@ -83,8 +83,8 @@ export default class DelegateConnection extends Connection { return this._delegate.protocol() } - connect (userAgent, authToken, waitReAuth) { - return this._delegate.connect(userAgent, authToken, waitReAuth) + connect (userAgent, boltAgent, authToken, waitReAuth) { + return this._delegate.connect(userAgent, authToken, boltAgent, waitReAuth) } write (message, observer, flush) { diff --git a/packages/bolt-connection/src/connection/connection.js b/packages/bolt-connection/src/connection/connection.js index 9d1107023..c0e1dc947 100644 --- a/packages/bolt-connection/src/connection/connection.js +++ b/packages/bolt-connection/src/connection/connection.js @@ -90,10 +90,11 @@ export default class Connection { /** * Connect to the target address, negotiate Bolt protocol and send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { + connect (userAgent, boltAgent, authToken) { throw new Error('not implemented') } diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js new file mode 100644 index 000000000..6be3593e7 --- /dev/null +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js @@ -0,0 +1,1104 @@ +/** + * 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. + */ + +import BoltProtocolV5x3 from '../../src/bolt/bolt-protocol-v5x3' +import RequestMessage from '../../src/bolt/request-message' +import { v2, structure } from '../../src/packstream' +import utils from '../test-utils' +import { LoginObserver, RouteObserver } from '../../src/bolt/stream-observers' +import fc from 'fast-check' +import { + Date, + DateTime, + Duration, + LocalDateTime, + LocalTime, + Path, + PathSegment, + Point, + Relationship, + Time, + UnboundRelationship, + Node, + internal +} from 'neo4j-driver-core' + +import { alloc } from '../../src/channel' +import { notificationFilterBehaviour } from './behaviour' + +const WRITE = 'WRITE' + +const { + txConfig: { TxConfig }, + bookmarks: { Bookmarks }, + logger: { Logger }, + temporalUtil +} = internal + +describe('#unit BoltProtocolV5x3', () => { + beforeEach(() => { + expect.extend(utils.matchers) + }) + + it('should request routing information', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + const routingContext = { someContextParam: 'value' } + const databaseName = 'name' + + const observer = protocol.requestRoutingInformation({ + routingContext, + databaseName + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.routeV4x4(routingContext, [], { databaseName, impersonatedUser: null }) + ) + expect(protocol.observers).toEqual([observer]) + expect(observer).toEqual(expect.any(RouteObserver)) + expect(protocol.flushes).toEqual([true]) + }) + + it('should request routing information sending bookmarks', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + const routingContext = { someContextParam: 'value' } + const listOfBookmarks = ['a', 'b', 'c'] + const bookmarks = new Bookmarks(listOfBookmarks) + const databaseName = 'name' + + const observer = protocol.requestRoutingInformation({ + routingContext, + databaseName, + sessionContext: { bookmarks } + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.routeV4x4(routingContext, listOfBookmarks, { databaseName, impersonatedUser: null }) + ) + expect(protocol.observers).toEqual([observer]) + expect(observer).toEqual(expect.any(RouteObserver)) + expect(protocol.flushes).toEqual([true]) + }) + + it('should run a query', () => { + const database = 'testdb' + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx2' + ]) + const txConfig = new TxConfig({ + timeout: 5000, + metadata: { x: 1, y: 'something' } + }) + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const query = 'RETURN $x, $y' + const parameters = { x: 'x', y: 'y' } + + const observer = protocol.run(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE + }) + + protocol.verifyMessageCount(2) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE + }) + ) + expect(protocol.messages[1]).toBeMessage(RequestMessage.pull()) + expect(protocol.observers).toEqual([observer, observer]) + expect(protocol.flushes).toEqual([false, true]) + }) + + it('should run a with impersonated user', () => { + const database = 'testdb' + const impersonatedUser = 'the impostor' + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx2' + ]) + const txConfig = new TxConfig({ + timeout: 5000, + metadata: { x: 1, y: 'something' } + }) + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const query = 'RETURN $x, $y' + const parameters = { x: 'x', y: 'y' } + + const observer = protocol.run(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE, + impersonatedUser + }) + + protocol.verifyMessageCount(2) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE, + impersonatedUser + }) + ) + expect(protocol.messages[1]).toBeMessage(RequestMessage.pull()) + expect(protocol.observers).toEqual([observer, observer]) + expect(protocol.flushes).toEqual([false, true]) + }) + + it('should begin a transaction', () => { + const database = 'testdb' + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx2' + ]) + const txConfig = new TxConfig({ + timeout: 5000, + metadata: { x: 1, y: 'something' } + }) + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.beginTransaction({ + bookmarks, + txConfig, + database, + mode: WRITE + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.begin({ bookmarks, txConfig, database, mode: WRITE }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should begin a transaction with impersonated user', () => { + const database = 'testdb' + const impersonatedUser = 'the impostor' + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx2' + ]) + const txConfig = new TxConfig({ + timeout: 5000, + metadata: { x: 1, y: 'something' } + }) + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.beginTransaction({ + bookmarks, + txConfig, + database, + mode: WRITE, + impersonatedUser + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.begin({ bookmarks, txConfig, database, mode: WRITE, impersonatedUser }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should return correct bolt version number', () => { + const protocol = new BoltProtocolV5x3(null, null, false) + + expect(protocol.version).toBe(5.2) + }) + + it('should update metadata', () => { + const metadata = { t_first: 1, t_last: 2, db_hits: 3, some_other_key: 4 } + const protocol = new BoltProtocolV5x3(null, null, false) + + const transformedMetadata = protocol.transformMetadata(metadata) + + expect(transformedMetadata).toEqual({ + result_available_after: 1, + result_consumed_after: 2, + db_hits: 3, + some_other_key: 4 + }) + }) + + it('should initialize connection', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const boltAgent = 'js-driver/1.2.3 (bolt agent)' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent: clientName, boltAgent: boltAgent, authToken }) + + protocol.verifyMessageCount(2) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello5x3(clientName, boltAgent) + ) + expect(protocol.messages[1]).toBeMessage( + RequestMessage.logon(authToken) + ) + + expect(protocol.observers.length).toBe(2) + + // hello observer + const helloObserver = protocol.observers[0] + expect(helloObserver).toBeInstanceOf(LoginObserver) + expect(helloObserver).not.toBe(observer) + + // login observer + const loginObserver = protocol.observers[1] + expect(loginObserver).toBeInstanceOf(LoginObserver) + expect(loginObserver).toBe(observer) + + expect(protocol.flushes).toEqual([false, true]) + }) + + it.each( + [true, false] + )('should logon to the server [flush=%s]', (flush) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.logon({ authToken, flush }) + + protocol.verifyMessageCount(1) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.logon(authToken) + ) + + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([flush]) + }) + + it.each( + [true, false] + )('should logoff from the server [flush=%s]', (flush) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.logoff({ flush }) + + protocol.verifyMessageCount(1) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.logoff() + ) + + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([flush]) + }) + + it('should begin a transaction', () => { + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx2' + ]) + const txConfig = new TxConfig({ + timeout: 5000, + metadata: { x: 1, y: 'something' } + }) + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.beginTransaction({ + bookmarks, + txConfig, + mode: WRITE + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.begin({ bookmarks, txConfig, mode: WRITE }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should commit', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.commitTransaction() + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage(RequestMessage.commit()) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should rollback', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.rollbackTransaction() + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage(RequestMessage.rollback()) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should support logoff', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + + expect(protocol.supportsReAuth).toBe(true) + }) + + describe('unpacker configuration', () => { + test.each([ + [false, false], + [false, true], + [true, false], + [true, true] + ])( + 'should create unpacker with disableLosslessIntegers=%p and useBigInt=%p', + (disableLosslessIntegers, useBigInt) => { + const protocol = new BoltProtocolV5x3(null, null, { + disableLosslessIntegers, + useBigInt + }) + expect(protocol._unpacker._disableLosslessIntegers).toBe( + disableLosslessIntegers + ) + expect(protocol._unpacker._useBigInt).toBe(useBigInt) + } + ) + }) + + describe('notificationFilter', () => { + notificationFilterBehaviour.shouldSupportNotificationFilterOnInitialize(newProtocol) + notificationFilterBehaviour.shouldSupportNotificationFilterOnBeginTransaction(newProtocol) + notificationFilterBehaviour.shouldSupportNotificationFilterOnRun(newProtocol) + }) + + describe('watermarks', () => { + it('.run() should configure watermarks', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = utils.spyProtocolWrite( + new BoltProtocolV5x3(recorder, null, false) + ) + + const query = 'RETURN $x, $y' + const parameters = { x: 'x', y: 'y' } + const observer = protocol.run(query, parameters, { + bookmarks: Bookmarks.empty(), + txConfig: TxConfig.empty(), + lowRecordWatermark: 100, + highRecordWatermark: 200 + }) + + expect(observer._lowRecordWatermark).toEqual(100) + expect(observer._highRecordWatermark).toEqual(200) + }) + }) + + describe('packstream', () => { + it('should configure v2 packer', () => { + const protocol = new BoltProtocolV5x3(null, null, false) + expect(protocol.packer()).toBeInstanceOf(v2.Packer) + }) + + it('should configure v2 unpacker', () => { + const protocol = new BoltProtocolV5x3(null, null, false) + expect(protocol.unpacker()).toBeInstanceOf(v2.Unpacker) + }) + }) + + describe('.packable()', () => { + it.each([ + ['Node', new Node(1, ['a'], { a: 'b' }, 'c')], + ['Relationship', new Relationship(1, 2, 3, 'a', { b: 'c' }, 'd', 'e', 'f')], + ['UnboundRelationship', new UnboundRelationship(1, 'a', { b: 'c' }, '1')], + ['Path', new Path(new Node(1, [], {}), new Node(2, [], {}), [])] + ])('should pack not pack graph types (%s)', (_, graphType) => { + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + null, + false + ) + + const packable = protocol.packable(graphType) + + expect(packable).toThrowErrorMatchingSnapshot() + }) + + it.each([ + ['Duration', new Duration(1, 1, 1, 1)], + ['LocalTime', new LocalTime(1, 1, 1, 1)], + ['Time', new Time(1, 1, 1, 1, 1)], + ['Date', new Date(1, 1, 1)], + ['LocalDateTime', new LocalDateTime(1, 1, 1, 1, 1, 1, 1)], + [ + 'DateTimeWithZoneOffset', + new DateTime(2022, 6, 14, 15, 21, 18, 183_000_000, 120 * 60) + ], + [ + 'DateTimeWithZoneOffset / 1978', + new DateTime(1978, 12, 16, 10, 5, 59, 128000987, -150 * 60) + ], + [ + 'DateTimeWithZoneId / Berlin 2:30 CET', + new DateTime(2022, 10, 30, 2, 30, 0, 183_000_000, 2 * 60 * 60, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Berlin 2:30 CEST', + new DateTime(2022, 10, 30, 2, 30, 0, 183_000_000, 1 * 60 * 60, 'Europe/Berlin') + ], + ['Point2D', new Point(1, 1, 1)], + ['Point3D', new Point(1, 1, 1, 1)] + ])('should pack spatial types and temporal types (%s)', (_, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + + expect(unpacked).toEqual(object) + }) + + it.each([ + [ + 'DateTimeWithZoneId / Australia', + new DateTime(2022, 6, 15, 15, 21, 18, 183_000_000, undefined, 'Australia/Eucla') + ], + [ + 'DateTimeWithZoneId', + new DateTime(2022, 6, 22, 15, 21, 18, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just before turn CEST', + new DateTime(2022, 3, 27, 1, 59, 59, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just 1 before turn CEST', + new DateTime(2022, 3, 27, 0, 59, 59, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just after turn CEST', + new DateTime(2022, 3, 27, 3, 0, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just 1 after turn CEST', + new DateTime(2022, 3, 27, 4, 0, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just before turn CET', + new DateTime(2022, 10, 30, 2, 59, 59, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just 1 before turn CET', + new DateTime(2022, 10, 30, 1, 59, 59, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just after turn CET', + new DateTime(2022, 10, 30, 3, 0, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Europe just 1 after turn CET', + new DateTime(2022, 10, 30, 4, 0, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just before turn summer time', + new DateTime(2018, 11, 4, 11, 59, 59, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just 1 before turn summer time', + new DateTime(2018, 11, 4, 10, 59, 59, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just after turn summer time', + new DateTime(2018, 11, 5, 1, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just 1 after turn summer time', + new DateTime(2018, 11, 5, 2, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just before turn winter time', + new DateTime(2019, 2, 17, 11, 59, 59, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just 1 before turn winter time', + new DateTime(2019, 2, 17, 10, 59, 59, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just after turn winter time', + new DateTime(2019, 2, 18, 0, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Sao Paulo just 1 after turn winter time', + new DateTime(2019, 2, 18, 1, 0, 0, 183_000_000, undefined, 'America/Sao_Paulo') + ], + [ + 'DateTimeWithZoneId / Istanbul', + new DateTime(1978, 12, 16, 12, 35, 59, 128000987, undefined, 'Europe/Istanbul') + ], + [ + 'DateTimeWithZoneId / Istanbul', + new DateTime(2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Pacific/Honolulu') + ], + [ + 'DateWithWithZoneId / Berlin before common era', + new DateTime(-2020, 6, 15, 4, 30, 0, 183_000_000, undefined, 'Europe/Berlin') + ], + [ + 'DateWithWithZoneId / Max Date', + new DateTime(99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Kiritimati') + ], + [ + 'DateWithWithZoneId / Min Date', + new DateTime(-99_999, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Samoa') + ], + [ + 'DateWithWithZoneId / Ambiguous date between 00 and 99', + new DateTime(50, 12, 31, 23, 59, 59, 999_999_999, undefined, 'Pacific/Samoa') + ] + ])('should pack and unpack DateTimeWithZoneId and without offset (%s)', (_, object) => { + const buffer = alloc(256) + const loggerFunction = jest.fn() + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + }, + undefined, + new Logger('debug', loggerFunction) + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + expect(loggerFunction) + .toBeCalledWith('warn', + 'DateTime objects without "timeZoneOffsetSeconds" property ' + + 'are prune to bugs related to ambiguous times. For instance, ' + + '2022-10-30T2:30:00[Europe/Berlin] could be GMT+1 or GMT+2.') + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + + expect(unpacked.timeZoneOffsetSeconds).toBeDefined() + + const unpackedDateTimeWithoutOffset = new DateTime( + unpacked.year, + unpacked.month, + unpacked.day, + unpacked.hour, + unpacked.minute, + unpacked.second, + unpacked.nanosecond, + undefined, + unpacked.timeZoneId + ) + + expect(unpackedDateTimeWithoutOffset).toEqual(object) + }) + + it('should pack and unpack DateTimeWithOffset', () => { + fc.assert( + fc.property( + fc.date({ + min: temporalUtil.newDate(utils.MIN_UTC_IN_MS + utils.ONE_DAY_IN_MS), + max: temporalUtil.newDate(utils.MAX_UTC_IN_MS - utils.ONE_DAY_IN_MS) + }), + fc.integer({ min: 0, max: 999_999 }), + utils.arbitraryTimeZoneId(), + (date, nanoseconds, timeZoneId) => { + const object = new DateTime( + date.getUTCFullYear(), + date.getUTCMonth() + 1, + date.getUTCDate(), + date.getUTCHours(), + date.getUTCMinutes(), + date.getUTCSeconds(), + date.getUTCMilliseconds() * 1_000_000 + nanoseconds, + undefined, + timeZoneId + ) + const buffer = alloc(256) + const loggerFunction = jest.fn() + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + }, + undefined, + new Logger('debug', loggerFunction) + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + expect(loggerFunction) + .toBeCalledWith('warn', + 'DateTime objects without "timeZoneOffsetSeconds" property ' + + 'are prune to bugs related to ambiguous times. For instance, ' + + '2022-10-30T2:30:00[Europe/Berlin] could be GMT+1 or GMT+2.') + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + + expect(unpacked.timeZoneOffsetSeconds).toBeDefined() + + const unpackedDateTimeWithoutOffset = new DateTime( + unpacked.year, + unpacked.month, + unpacked.day, + unpacked.hour, + unpacked.minute, + unpacked.second, + unpacked.nanosecond, + undefined, + unpacked.timeZoneId + ) + + expect(unpackedDateTimeWithoutOffset).toEqual(object) + }) + ) + }) + + it('should pack and unpack DateTimeWithZoneIdAndNoOffset', () => { + fc.assert( + fc.property(fc.date(), date => { + const object = DateTime.fromStandardDate(date) + const buffer = alloc(256) + const loggerFunction = jest.fn() + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + }, + undefined, + new Logger('debug', loggerFunction) + ) + + const packable = protocol.packable(object) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + + expect(unpacked.timeZoneOffsetSeconds).toBeDefined() + + expect(unpacked).toEqual(object) + }) + ) + }) + }) + + describe('.unpack()', () => { + it.each([ + [ + 'Node', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, 'elementId']), + new Node(1, ['a'], { c: 'd' }, 'elementId') + ], + [ + 'Relationship', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, 'elementId', 'node1', 'node2']), + new Relationship(1, 2, 3, '4', { 5: 6 }, 'elementId', 'node1', 'node2') + ], + [ + 'UnboundRelationship', + new structure.Structure(0x72, [1, '2', { 3: 4 }, 'elementId']), + new UnboundRelationship(1, '2', { 3: 4 }, 'elementId') + ], + [ + 'Path', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }, 'node1']), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }, 'node2']), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }, 'node3']) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'reltype1', { 4: '5' }, 'rel1', 'node1', 'node2']), + new structure.Structure(0x52, [5, 4, 2, 'reltype2', { 6: 7 }, 'rel2', 'node2', 'node3']) + ], + [1, 1, 2, 2] + ] + ), + new Path( + new Node(1, ['2'], { 3: '4' }, 'node1'), + new Node(2, ['3'], { 4: '5' }, 'node3'), + [ + new PathSegment( + new Node(1, ['2'], { 3: '4' }, 'node1'), + new Relationship(3, 1, 4, 'reltype1', { 4: '5' }, 'rel1', 'node1', 'node2'), + new Node(4, ['5'], { 6: 7 }, 'node2') + ), + new PathSegment( + new Node(4, ['5'], { 6: 7 }, 'node2'), + new Relationship(5, 4, 2, 'reltype2', { 6: 7 }, 'rel2', 'node2', 'node3'), + new Node(2, ['3'], { 4: '5' }, 'node3') + ) + ] + ) + ] + ])('should unpack graph types (%s)', (_, struct, graphObject) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(graphObject) + }) + + it.each([ + [ + 'Node with less fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }]) + ], + [ + 'Node with more fields', + new structure.Structure(0x4e, [1, ['a'], { c: 'd' }, '1', 'b']) + ], + [ + 'Relationship with less fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }]) + ], + [ + 'Relationship with more fields', + new structure.Structure(0x52, [1, 2, 3, '4', { 5: 6 }, '1', '2', '3', '4']) + ], + [ + 'UnboundRelationship with less fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }]) + ], + [ + 'UnboundRelationship with more fields', + new structure.Structure(0x72, [1, '2', { 3: 4 }, '1', '2']) + ], + [ + 'Path with less fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ] + ] + ) + ], + [ + 'Path with more fields', + new structure.Structure( + 0x50, + [ + [ + new structure.Structure(0x4e, [1, ['2'], { 3: '4' }]), + new structure.Structure(0x4e, [4, ['5'], { 6: 7 }]), + new structure.Structure(0x4e, [2, ['3'], { 4: '5' }]) + ], + [ + new structure.Structure(0x52, [3, 1, 4, 'rel1', { 4: '5' }]), + new structure.Structure(0x52, [5, 4, 2, 'rel2', { 6: 7 }]) + ], + [1, 1, 2, 2], + 'a' + ] + ) + ], + [ + 'Point with less fields', + new structure.Structure(0x58, [1, 2]) + ], + [ + 'Point with more fields', + new structure.Structure(0x58, [1, 2, 3, 4]) + ], + [ + 'Point3D with less fields', + new structure.Structure(0x59, [1, 2, 3]) + ], + + [ + 'Point3D with more fields', + new structure.Structure(0x59, [1, 2, 3, 4, 6]) + ], + [ + 'Duration with less fields', + new structure.Structure(0x45, [1, 2, 3]) + ], + [ + 'Duration with more fields', + new structure.Structure(0x45, [1, 2, 3, 4, 5]) + ], + [ + 'LocalTime with less fields', + new structure.Structure(0x74, []) + ], + [ + 'LocalTime with more fields', + new structure.Structure(0x74, [1, 2]) + ], + [ + 'Time with less fields', + new structure.Structure(0x54, [1]) + ], + [ + 'Time with more fileds', + new structure.Structure(0x54, [1, 2, 3]) + ], + [ + 'Date with less fields', + new structure.Structure(0x44, []) + ], + [ + 'Date with more fields', + new structure.Structure(0x44, [1, 2]) + ], + [ + 'LocalDateTime with less fields', + new structure.Structure(0x64, [1]) + ], + [ + 'LocalDateTime with more fields', + new structure.Structure(0x64, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneOffset with less fields', + new structure.Structure(0x49, [1, 2]) + ], + [ + 'DateTimeWithZoneOffset with more fields', + new structure.Structure(0x49, [1, 2, 3, 4]) + ], + [ + 'DateTimeWithZoneId with less fields', + new structure.Structure(0x69, [1, 2]) + ], + [ + 'DateTimeWithZoneId with more fields', + new structure.Structure(0x69, [1, 2, 'America/Sao Paulo', 'Brasil']) + ] + ])('should not unpack with wrong size (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + false + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(() => unpacked instanceof structure.Structure).toThrowErrorMatchingSnapshot() + }) + + it.each([ + [ + 'Point', + new structure.Structure(0x58, [1, 2, 3]), + new Point(1, 2, 3) + ], + [ + 'Point3D', + new structure.Structure(0x59, [1, 2, 3, 4]), + new Point(1, 2, 3, 4) + ], + [ + 'Duration', + new structure.Structure(0x45, [1, 2, 3, 4]), + new Duration(1, 2, 3, 4) + ], + [ + 'LocalTime', + new structure.Structure(0x74, [1]), + new LocalTime(0, 0, 0, 1) + ], + [ + 'Time', + new structure.Structure(0x54, [1, 2]), + new Time(0, 0, 0, 1, 2) + ], + [ + 'Date', + new structure.Structure(0x44, [1]), + new Date(1970, 1, 2) + ], + [ + 'LocalDateTime', + new structure.Structure(0x64, [1, 2]), + new LocalDateTime(1970, 1, 1, 0, 0, 1, 2) + ], + [ + 'DateTimeWithZoneOffset', + new structure.Structure(0x49, [ + 1655212878, 183_000_000, 120 * 60 + ]), + new DateTime(2022, 6, 14, 15, 21, 18, 183_000_000, 120 * 60) + ], + [ + 'DateTimeWithZoneOffset / 1978', + new structure.Structure(0x49, [ + 282659759, 128000987, -150 * 60 + ]), + new DateTime(1978, 12, 16, 10, 5, 59, 128000987, -150 * 60) + ], + [ + 'DateTimeWithZoneId', + new structure.Structure(0x69, [ + 1655212878, 183_000_000, 'Europe/Berlin' + ]), + new DateTime(2022, 6, 14, 15, 21, 18, 183_000_000, 2 * 60 * 60, 'Europe/Berlin') + ], + [ + 'DateTimeWithZoneId / Australia', + new structure.Structure(0x69, [ + 1655212878, 183_000_000, 'Australia/Eucla' + ]), + new DateTime(2022, 6, 14, 22, 6, 18, 183_000_000, 8 * 60 * 60 + 45 * 60, 'Australia/Eucla') + ], + [ + 'DateTimeWithZoneId / Honolulu', + new structure.Structure(0x69, [ + 1592231400, 183_000_000, 'Pacific/Honolulu' + ]), + new DateTime(2020, 6, 15, 4, 30, 0, 183_000_000, -10 * 60 * 60, 'Pacific/Honolulu') + ] + ])('should unpack spatial types and temporal types (%s)', (_, struct, object) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(object) + }) + + it.each([ + [ + 'DateTimeWithZoneOffset/0x46', + new structure.Structure(0x46, [1, 2, 3]) + ], + [ + 'DateTimeWithZoneId/0x66', + new structure.Structure(0x66, [1, 2, 'America/Sao_Paulo']) + ] + ])('should unpack deprecated temporal types as unknown structs (%s)', (_, struct) => { + const buffer = alloc(256) + const protocol = new BoltProtocolV5x3( + new utils.MessageRecordingConnection(), + buffer, + { + disableLosslessIntegers: true + } + ) + + const packable = protocol.packable(struct) + + expect(packable).not.toThrow() + + buffer.reset() + + const unpacked = protocol.unpack(buffer) + expect(unpacked).toEqual(struct) + }) + }) + + function newProtocol (recorder) { + return new BoltProtocolV5x3(recorder, null, false, undefined, undefined, () => {}) + } +}) diff --git a/packages/bolt-connection/test/bolt/index.test.js b/packages/bolt-connection/test/bolt/index.test.js index 1d550e7c8..98106391d 100644 --- a/packages/bolt-connection/test/bolt/index.test.js +++ b/packages/bolt-connection/test/bolt/index.test.js @@ -33,6 +33,7 @@ import BoltProtocolV4x4 from '../../src/bolt/bolt-protocol-v4x4' import BoltProtocolV5x0 from '../../src/bolt/bolt-protocol-v5x0' import BoltProtocolV5x1 from '../../src/bolt/bolt-protocol-v5x1' import BoltProtocolV5x2 from '../../src/bolt/bolt-protocol-v5x2' +import BoltProtocolV5x3 from '../../src/bolt/bolt-protocol-v5x3' const { logger: { Logger } @@ -46,13 +47,13 @@ describe('#unit Bolt', () => { const writtenBuffer = channel.written[0] const boltMagicPreamble = '60 60 b0 17' - const protocolVersion5x2to5x0 = '00 02 02 05' + const protocolVersion5x3to5x0 = '00 03 03 05' const protocolVersion4x4to4x2 = '00 02 04 04' const protocolVersion4x1 = '00 00 01 04' const protocolVersion3 = '00 00 00 03' expect(writtenBuffer.toHex()).toEqual( - `${boltMagicPreamble} ${protocolVersion5x2to5x0} ${protocolVersion4x4to4x2} ${protocolVersion4x1} ${protocolVersion3}` + `${boltMagicPreamble} ${protocolVersion5x3to5x0} ${protocolVersion4x4to4x2} ${protocolVersion4x1} ${protocolVersion3}` ) }) @@ -363,7 +364,8 @@ describe('#unit Bolt', () => { v(4.4, BoltProtocolV4x4), v(5.0, BoltProtocolV5x0), v(5.1, BoltProtocolV5x1), - v(5.2, BoltProtocolV5x2) + v(5.2, BoltProtocolV5x2), + v(5.3, BoltProtocolV5x3) ] availableProtocols.forEach(lambda) diff --git a/packages/core/package.json b/packages/core/package.json index 175b0198e..62f20efec 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -24,6 +24,9 @@ "neo4j", "driver" ], + "browser": { + "./lib/internal/bolt-agent/index.js": "./lib/internal/bolt-agent/browser/index.js" + }, "author": "Neo4j", "license": "Apache-2.0", "bugs": { diff --git a/packages/core/src/connection.ts b/packages/core/src/connection.ts index 80749db12..5782b7c93 100644 --- a/packages/core/src/connection.ts +++ b/packages/core/src/connection.ts @@ -83,10 +83,12 @@ class Connection { /** * Connect to the target address, negotiate Bolt protocol and send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {Object} waitReAuth whether to connect method should wait until re-Authorised * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent: string, authToken: any): Promise { + connect (userAgent: string, boltAgent: string, authToken: any, waitReAuth: false): Promise { throw Error('Not implemented') } diff --git a/packages/core/src/internal/bolt-agent/browser/BoltAgent.ts b/packages/core/src/internal/bolt-agent/browser/BoltAgent.ts new file mode 100644 index 000000000..12febc2c9 --- /dev/null +++ b/packages/core/src/internal/bolt-agent/browser/BoltAgent.ts @@ -0,0 +1,21 @@ +/** +* 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. +*/ +export function fromVersion (version: string): string { + return `neo4j-javascript/${version} (browser)}` +} diff --git a/packages/core/src/internal/bolt-agent/browser/index.ts b/packages/core/src/internal/bolt-agent/browser/index.ts new file mode 100644 index 000000000..5644343ce --- /dev/null +++ b/packages/core/src/internal/bolt-agent/browser/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './BoltAgent' diff --git a/packages/core/src/internal/bolt-agent/deno/BoltAgent.ts b/packages/core/src/internal/bolt-agent/deno/BoltAgent.ts new file mode 100644 index 000000000..0fe31ebb2 --- /dev/null +++ b/packages/core/src/internal/bolt-agent/deno/BoltAgent.ts @@ -0,0 +1,21 @@ +/** +* 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. +*/ +export function fromVersion (version: string): string { + return `neo4j-javascript/${version} (browser)` +} diff --git a/packages/core/src/internal/bolt-agent/deno/index.ts b/packages/core/src/internal/bolt-agent/deno/index.ts new file mode 100644 index 000000000..5644343ce --- /dev/null +++ b/packages/core/src/internal/bolt-agent/deno/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './BoltAgent' diff --git a/packages/core/src/internal/bolt-agent/index.ts b/packages/core/src/internal/bolt-agent/index.ts new file mode 100644 index 000000000..aa540c72f --- /dev/null +++ b/packages/core/src/internal/bolt-agent/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './node/index' diff --git a/packages/core/src/internal/bolt-agent/node/BoltAgent.ts b/packages/core/src/internal/bolt-agent/node/BoltAgent.ts new file mode 100644 index 000000000..d639f1fce --- /dev/null +++ b/packages/core/src/internal/bolt-agent/node/BoltAgent.ts @@ -0,0 +1,44 @@ +/** +* 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. +*/ +import * as os from 'os' + +export function fromVersion (version: string): string { + const HOST_ARCH = process.config.variables.host_arch + const NODE_VERSION = 'Node/' + process.versions.node + const NODE_V8_VERSION = process.versions.v8 + + const osName = mapOs(os.platform()) + + return `neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` +} + +export function mapOs (osType: string): string { + let osName + if (osType === 'darwin') { + osName = 'MacOS' + } else if (osType === 'win32') { + osName = 'Windows' + } else if (osType === 'linux') { + osName = 'Linux' + } else { + osName = osType + } + + return osName +} diff --git a/packages/core/src/internal/bolt-agent/node/index.ts b/packages/core/src/internal/bolt-agent/node/index.ts new file mode 100644 index 000000000..5644343ce --- /dev/null +++ b/packages/core/src/internal/bolt-agent/node/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './BoltAgent' diff --git a/packages/core/src/internal/constants.ts b/packages/core/src/internal/constants.ts index d42d1da1c..2c56e9f1b 100644 --- a/packages/core/src/internal/constants.ts +++ b/packages/core/src/internal/constants.ts @@ -36,6 +36,7 @@ const BOLT_PROTOCOL_V4_4: number = 4.4 const BOLT_PROTOCOL_V5_0: number = 5.0 const BOLT_PROTOCOL_V5_1: number = 5.1 const BOLT_PROTOCOL_V5_2: number = 5.2 +const BOLT_PROTOCOL_V5_3: number = 5.3 export { FETCH_ALL, @@ -54,5 +55,6 @@ export { BOLT_PROTOCOL_V4_4, BOLT_PROTOCOL_V5_0, BOLT_PROTOCOL_V5_1, - BOLT_PROTOCOL_V5_2 + BOLT_PROTOCOL_V5_2, + BOLT_PROTOCOL_V5_3 } diff --git a/packages/core/src/internal/index.ts b/packages/core/src/internal/index.ts index ba8515a39..e662d1c74 100644 --- a/packages/core/src/internal/index.ts +++ b/packages/core/src/internal/index.ts @@ -30,6 +30,7 @@ import * as urlUtil from './url-util' import * as serverAddress from './server-address' import * as resolver from './resolver' import * as objectUtil from './object-util' +import * as boltAgent from './bolt-agent/index' export { util, @@ -44,5 +45,6 @@ export { urlUtil, serverAddress, resolver, - objectUtil + objectUtil, + boltAgent } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 463dd4713..c509145d3 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -65,6 +65,7 @@ export interface Config { logging?: LoggingConfig resolver?: (address: string) => string[] | Promise userAgent?: string + boltAgent?: string } /** diff --git a/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts new file mode 100644 index 000000000..dd2094c02 --- /dev/null +++ b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts @@ -0,0 +1,43 @@ +/** + * 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. + */ +import { boltAgent as BoltAgent } from '../../../../src/internal' +import os from 'os' + +describe('#unit boltAgent', () => { + // This test is very fragile but the exact look of this string should not change without PM approval + it('should return the correct bolt agent for specified version', () => { + const version = '5.3' + const boltAgent = BoltAgent.fromVersion(version) + + const HOST_ARCH = process.config.variables.host_arch + const NODE_VERSION = 'Node/' + process.versions.node + const NODE_V8_VERSION = process.versions.v8 + + const osName = BoltAgent.mapOs(os.platform()) + + expect(boltAgent.length === 0).toBeFalsy() + expect(boltAgent).toContain(`neo4j-javascript/${version}`) + expect(boltAgent).toContain(`${HOST_ARCH}`) + expect(boltAgent).toContain(`${NODE_VERSION}`) + expect(boltAgent).toContain(`${NODE_V8_VERSION}`) + expect(boltAgent).toContain(`${osName}`) + + expect(boltAgent).toEqual(`neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})`) + }) +}) diff --git a/packages/neo4j-driver-deno/generate.ts b/packages/neo4j-driver-deno/generate.ts index 582f6c463..96079dd0a 100644 --- a/packages/neo4j-driver-deno/generate.ts +++ b/packages/neo4j-driver-deno/generate.ts @@ -129,15 +129,15 @@ async function copyAndTransform(inDir: string, outDir: string) { } } - // Special fix for bolt-connection/channel/index.js + // Special fix for bolt-connection/channel/index.js and core/bolt-agent/index.js // Replace the "node channel" with the "deno channel", since Deno supports different APIs - if (inPath.endsWith("channel/index.js")) { + // Replace the "node boltAgent" with the "deno boltAgent", since Deno supports different APIs + if (inPath.endsWith("channel/index.js") || inPath.endsWith("bolt-agent/index.ts")) { contents = contents.replace( - `export * from './node/index.js'`, - `export * from './deno/index.js'`, + `export * from './node/index'`, + `export * from './deno/index'`, ); } - } await Deno.writeTextFile(outPath, contents); diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js index 9f8cc81bf..8467afa93 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js @@ -24,11 +24,12 @@ import { object } from '../lang/index.js' * Class which provides Authorization for {@link Connection} */ export default class AuthenticationProvider { - constructor ({ authTokenManager, userAgent }) { + constructor ({ authTokenManager, userAgent, boltAgent }) { this._authTokenManager = authTokenManager || expirationBasedAuthTokenManager({ tokenProvider: () => {} }) this._userAgent = userAgent + this._boltAgent = boltAgent } async authenticate ({ connection, auth, skipReAuth, waitReAuth, forceReAuth }) { @@ -38,7 +39,7 @@ export default class AuthenticationProvider { forceReAuth === true ) if (connection.authToken == null || shouldReAuth) { - return await connection.connect(this._userAgent, auth, waitReAuth || false) + return await connection.connect(this._userAgent, this._boltAgent, auth, waitReAuth || false) } return connection } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js index 0b0fc5bfa..bd4e45a4a 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js @@ -33,7 +33,7 @@ const AUTHENTICATION_ERRORS = [ export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, + { id, config, log, userAgent, boltAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, createChannelConnectionHook = null ) { super() @@ -42,7 +42,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._config = config this._log = log this._authTokenManager = authTokenManager - this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent }) + this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent, boltAgent }) this._createChannelConnection = createChannelConnectionHook || (address => { @@ -67,7 +67,6 @@ export default class PooledConnectionProvider extends ConnectionProvider { config: PoolConfig.fromDriverConfig(config), log: this._log }) - this._userAgent = userAgent this._openConnections = {} } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js index a974d17c7..ea1b82d75 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js @@ -183,9 +183,11 @@ export default class ChannelConnection extends Connection { /** * Send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ +<<<<<<< HEAD async connect (userAgent, authToken, waitReAuth) { if (this._protocol.initialized && !this._protocol.supportsReAuth) { throw newError('Connection does not support re-auth') @@ -216,6 +218,10 @@ export default class ChannelConnection extends Connection { this._protocol.logon({ authToken, flush: true }) return this +======= + connect (userAgent, boltAgent, authToken) { + return this._initialize(userAgent, boltAgent, authToken) +>>>>>>> dce70ae8 (Bolt agent added to hello metadata) } /** @@ -224,12 +230,13 @@ export default class ChannelConnection extends Connection { * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if initialization is successful. Rejected promise otherwise. */ - _initialize (userAgent, authToken) { + _initialize (userAgent, boltAgent, authToken) { const self = this return new Promise((resolve, reject) => { this._protocol.initialize({ userAgent, authToken, + boltAgent, notificationFilter: this._notificationFilter, onError: err => reject(err), onComplete: metadata => { diff --git a/packages/neo4j-driver-deno/lib/core/types.ts b/packages/neo4j-driver-deno/lib/core/types.ts index 463dd4713..258ee38fd 100644 --- a/packages/neo4j-driver-deno/lib/core/types.ts +++ b/packages/neo4j-driver-deno/lib/core/types.ts @@ -64,7 +64,8 @@ export interface Config { useBigInt?: boolean logging?: LoggingConfig resolver?: (address: string) => string[] | Promise - userAgent?: string + userAgent?: string, + boltAgent?: string } /** diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 41fb4d73e..bc09e603c 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -335,7 +335,8 @@ function driver ( const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent ?? USER_AGENT + config.userAgent = config.userAgent ?? BOLT_AGENT + config.boltAgent = BOLT_AGENT const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -362,6 +363,7 @@ function driver ( authTokenManager, address, userAgent: config.userAgent, + boltAgent: config.boltAgent, routingContext: parsedUrl.query }) } else { @@ -378,7 +380,8 @@ function driver ( log, authTokenManager, address, - userAgent: config.userAgent + userAgent: config.userAgent, + boltAgent: config.boltAgent }) } } @@ -404,7 +407,7 @@ async function hasReachableServer (url: string, config?: Pick } } -const USER_AGENT: string = 'neo4j-javascript/' + VERSION +const BOLT_AGENT = internal.boltAgent.BoltAgent.fromVersion('neo4j-javascript/' + VERSION) /** * Object containing constructors for all neo4j types. diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 012faaf97..3997e3f00 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -298,8 +298,7 @@ function driver (url, authToken, config = {}) { const authTokenManager = createAuthManager(authToken) - // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent || USER_AGENT + config.boltAgent = BOLT_AGENT const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -321,6 +320,7 @@ function driver (url, authToken, config = {}) { authTokenManager, address, userAgent: config.userAgent, + boltAgent: config.boltAgent, routingContext: parsedUrl.query }) } else { @@ -337,7 +337,8 @@ function driver (url, authToken, config = {}) { log, authTokenManager, address, - userAgent: config.userAgent + userAgent: config.userAgent, + boltAgent: config.boltAgent }) } } @@ -363,7 +364,7 @@ async function hasReachableServer (url, config) { } } -const USER_AGENT = 'neo4j-javascript/' + VERSION +const BOLT_AGENT = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) /** * Object containing predefined logging configurations. These are expected to be used as values of the driver config's `logging` property. diff --git a/packages/testkit-backend/src/feature/common.js b/packages/testkit-backend/src/feature/common.js index 0a2235f98..5240ab630 100644 --- a/packages/testkit-backend/src/feature/common.js +++ b/packages/testkit-backend/src/feature/common.js @@ -21,6 +21,7 @@ const features = [ 'Feature:Bolt:5.0', 'Feature:Bolt:5.1', 'Feature:Bolt:5.2', + 'Feature:Bolt:5.3', 'Feature:Bolt:Patch:UTC', 'Feature:API:ConnectionAcquisitionTimeout', 'Feature:API:Driver.ExecuteQuery', From 93aec25c9d4b9945b98c0020ea935ee30f8a7238 Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Wed, 5 Apr 2023 16:07:51 +0100 Subject: [PATCH 03/18] Version 1 fix for user agent being empty --- packages/bolt-connection/src/bolt/bolt-protocol-v1.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index b7be12d3c..9922bd468 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -184,7 +184,7 @@ export default class BoltProtocol { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.init(userAgent || boltAgent, authToken), observer, true) + this.write(RequestMessage.init(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) return observer } From e94525592c196516de43a9742cba7fc06b0d17cd Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Thu, 6 Apr 2023 09:10:31 +0100 Subject: [PATCH 04/18] Fix useragent string on metadata object in 5.3 --- .../src/bolt/request-message.js | 6 +- .../browser/{BoltAgent.ts => bolt-agent.ts} | 0 .../src/internal/bolt-agent/browser/index.ts | 2 +- .../deno/{BoltAgent.ts => bolt-agent.ts} | 0 .../src/internal/bolt-agent/deno/index.ts | 2 +- .../core/src/internal/bolt-agent/index.ts | 2 +- .../node/{BoltAgent.ts => bolt-agent.ts} | 0 .../src/internal/bolt-agent/node/index.ts | 2 +- packages/neo4j-driver-deno/generate.ts | 22 +++- .../bolt-connection/bolt/bolt-protocol-v1.js | 4 +- .../bolt-connection/bolt/bolt-protocol-v3.js | 4 +- .../bolt/bolt-protocol-v4x1.js | 4 +- .../bolt/bolt-protocol-v4x3.js | 4 +- .../bolt/bolt-protocol-v5x0.js | 4 +- .../bolt/bolt-protocol-v5x1.js | 4 +- .../bolt/bolt-protocol-v5x2.js | 6 +- .../bolt/bolt-protocol-v5x3.js | 78 ++++++++++++ .../bolt/bolt-protocol-v5x3.transformer.js | 24 ++++ .../lib/bolt-connection/bolt/create.js | 9 ++ .../lib/bolt-connection/bolt/handshake.js | 2 +- .../bolt-connection/bolt/request-message.js | 36 ++++++ .../connection-provider-direct.js | 5 + .../connection-provider-pooled.js | 15 +++ .../connection-provider-routing.js | 8 ++ .../connection/connection-channel.js | 3 +- .../connection/connection-delegate.js | 5 + .../bolt-connection/connection/connection.js | 3 +- .../neo4j-driver-deno/lib/core/connection.ts | 3 +- .../internal/bolt-agent/browser/bolt-agent.ts | 21 ++++ .../core/internal/bolt-agent/browser/index.ts | 20 +++ .../internal/bolt-agent/deno/bolt-agent.ts | 21 ++++ .../core/internal/bolt-agent/deno/index.ts | 20 +++ .../lib/core/internal/bolt-agent/index.ts | 20 +++ .../internal/bolt-agent/node/bolt-agent.ts | 44 +++++++ .../core/internal/bolt-agent/node/index.ts | 20 +++ .../lib/core/internal/constants.ts | 4 +- .../lib/core/internal/index.ts | 4 +- packages/neo4j-driver-deno/lib/core/types.ts | 2 +- packages/neo4j-driver-deno/lib/mod.ts | 117 +++++++++--------- packages/neo4j-driver-lite/src/index.ts | 110 ++++++++-------- packages/neo4j-driver/src/index.js | 4 +- 41 files changed, 514 insertions(+), 150 deletions(-) rename packages/core/src/internal/bolt-agent/browser/{BoltAgent.ts => bolt-agent.ts} (100%) rename packages/core/src/internal/bolt-agent/deno/{BoltAgent.ts => bolt-agent.ts} (100%) rename packages/core/src/internal/bolt-agent/node/{BoltAgent.ts => bolt-agent.ts} (100%) create mode 100644 packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js create mode 100644 packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.transformer.js create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/index.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/index.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/index.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts create mode 100644 packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/index.ts diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index ddcc4cfdc..4da2e2a5e 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -200,7 +200,11 @@ export default class RequestMessage { * @return {RequestMessage} new HELLO message. */ static hello5x3 (userAgent, boltAgent, notificationFilter = null, routing = null) { - const metadata = { user_agent: userAgent, bolt_agent: boltAgent } + const metadata = { bolt_agent: boltAgent } + + if (userAgent) { + metadata.user_agent = userAgent + } if (notificationFilter) { if (notificationFilter.minimumSeverityLevel) { diff --git a/packages/core/src/internal/bolt-agent/browser/BoltAgent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts similarity index 100% rename from packages/core/src/internal/bolt-agent/browser/BoltAgent.ts rename to packages/core/src/internal/bolt-agent/browser/bolt-agent.ts diff --git a/packages/core/src/internal/bolt-agent/browser/index.ts b/packages/core/src/internal/bolt-agent/browser/index.ts index 5644343ce..088fdeb43 100644 --- a/packages/core/src/internal/bolt-agent/browser/index.ts +++ b/packages/core/src/internal/bolt-agent/browser/index.ts @@ -17,4 +17,4 @@ * limitations under the License. */ -export * from './BoltAgent' +export * from './bolt-agent' diff --git a/packages/core/src/internal/bolt-agent/deno/BoltAgent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts similarity index 100% rename from packages/core/src/internal/bolt-agent/deno/BoltAgent.ts rename to packages/core/src/internal/bolt-agent/deno/bolt-agent.ts diff --git a/packages/core/src/internal/bolt-agent/deno/index.ts b/packages/core/src/internal/bolt-agent/deno/index.ts index 5644343ce..088fdeb43 100644 --- a/packages/core/src/internal/bolt-agent/deno/index.ts +++ b/packages/core/src/internal/bolt-agent/deno/index.ts @@ -17,4 +17,4 @@ * limitations under the License. */ -export * from './BoltAgent' +export * from './bolt-agent' diff --git a/packages/core/src/internal/bolt-agent/index.ts b/packages/core/src/internal/bolt-agent/index.ts index aa540c72f..710495503 100644 --- a/packages/core/src/internal/bolt-agent/index.ts +++ b/packages/core/src/internal/bolt-agent/index.ts @@ -17,4 +17,4 @@ * limitations under the License. */ -export * from './node/index' +export * from './node' diff --git a/packages/core/src/internal/bolt-agent/node/BoltAgent.ts b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts similarity index 100% rename from packages/core/src/internal/bolt-agent/node/BoltAgent.ts rename to packages/core/src/internal/bolt-agent/node/bolt-agent.ts diff --git a/packages/core/src/internal/bolt-agent/node/index.ts b/packages/core/src/internal/bolt-agent/node/index.ts index 5644343ce..088fdeb43 100644 --- a/packages/core/src/internal/bolt-agent/node/index.ts +++ b/packages/core/src/internal/bolt-agent/node/index.ts @@ -17,4 +17,4 @@ * limitations under the License. */ -export * from './BoltAgent' +export * from './bolt-agent' diff --git a/packages/neo4j-driver-deno/generate.ts b/packages/neo4j-driver-deno/generate.ts index 96079dd0a..dc11f9579 100644 --- a/packages/neo4j-driver-deno/generate.ts +++ b/packages/neo4j-driver-deno/generate.ts @@ -129,13 +129,25 @@ async function copyAndTransform(inDir: string, outDir: string) { } } - // Special fix for bolt-connection/channel/index.js and core/bolt-agent/index.js - // Replace the "node channel" with the "deno channel", since Deno supports different APIs + // Special fix for core/internal/bolt-agent/index.js // Replace the "node boltAgent" with the "deno boltAgent", since Deno supports different APIs - if (inPath.endsWith("channel/index.js") || inPath.endsWith("bolt-agent/index.ts")) { + if(inPath.endsWith("bolt-agent/index.ts")){ + console.log(inPath) + console.log(contents) + contents = contents.replace( + `export * from './node/index.ts'`, + `export * from './deno/index.ts'`, + ); + } + + // Special fix for bolt-connection/channel/index.js and core/internal/bolt-agent/index.js + // Replace the "node channel" with the "deno channel", since Deno supports different APIs + if (inPath.endsWith("channel/index.js")) { + console.log(inPath) + console.log(contents) contents = contents.replace( - `export * from './node/index'`, - `export * from './deno/index'`, + `export * from './node/index.js'`, + `export * from './deno/index.js'`, ); } } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js index 1212bb019..5aceb8a47 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js @@ -175,7 +175,7 @@ export default class BoltProtocol { * @param {function()} param.onComplete the callback to invoke on completion. * @returns {StreamObserver} the stream observer that monitors the corresponding server response. */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) @@ -184,7 +184,7 @@ export default class BoltProtocol { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.init(userAgent, authToken), observer, true) + this.write(RequestMessage.init(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) return observer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js index 42b9d6788..381f703b3 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js @@ -69,7 +69,7 @@ export default class BoltProtocol extends BoltProtocolV2 { return metadata } - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -78,7 +78,7 @@ export default class BoltProtocol extends BoltProtocolV2 { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.hello(userAgent, authToken), observer, true) + this.write(RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) return observer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js index 8ab31730e..2fd503c41 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js @@ -73,7 +73,7 @@ export default class BoltProtocol extends BoltProtocolV4 { return this._transformer } - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -83,7 +83,7 @@ export default class BoltProtocol extends BoltProtocolV4 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js index dcee97cb5..2a913a1da 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js @@ -89,7 +89,7 @@ export default class BoltProtocol extends BoltProtocolV42 { * @param {function(onComplte)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => { @@ -104,7 +104,7 @@ export default class BoltProtocol extends BoltProtocolV42 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting, ['utc']), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js index cf4330563..bc489f7bf 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js @@ -53,7 +53,7 @@ export default class BoltProtocol extends BoltProtocolV44 { * @param {function(onComplte)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, authToken, onComplete) @@ -63,7 +63,7 @@ export default class BoltProtocol extends BoltProtocolV44 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js index 4c6dba634..610b9a54b 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js @@ -57,7 +57,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { * @param {function(onComplete)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const state = {} const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), @@ -71,7 +71,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello5x1(userAgent, this._serversideRouting), + RequestMessage.hello5x1(userAgent === '' || userAgent == null ? boltAgent : userAgent, this._serversideRouting), observer, false ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js index 9a77a3a10..ab1e2e869 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js @@ -50,13 +50,14 @@ export default class BoltProtocol extends BoltProtocolV5x1 { * * @param {Object} args The params * @param {string} args.userAgent The user agent + * @param {string} args.boltAgent The bolt agent * @param {any} args.authToken The auth token * @param {NotificationFilter} args.notificationFilter The notification filters. * @param {function(error)} args.onError On error callback * @param {function(onComplete)} args.onComplete On complete callback * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, notificationFilter, onError, onComplete } = {}) { + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { const state = {} const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), @@ -67,7 +68,8 @@ export default class BoltProtocol extends BoltProtocolV5x1 { }) this.write( - RequestMessage.hello5x2(userAgent, notificationFilter, this._serversideRouting), + // if useragent is null then for all versions before 5.3 it should be bolt agent by default + RequestMessage.hello5x2(userAgent === '' || userAgent == null ? boltAgent : userAgent, notificationFilter, this._serversideRouting), observer, false ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js new file mode 100644 index 000000000..f02b86caf --- /dev/null +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.js @@ -0,0 +1,78 @@ +/** + * 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. + */ +import BoltProtocolV5x2 from './bolt-protocol-v5x2.js' + +import transformersFactories from './bolt-protocol-v5x3.transformer.js' +import Transformer from './transformer.js' +import RequestMessage from './request-message.js' +import { LoginObserver } from './stream-observers.js' + +import { internal } from '../../core/index.ts' + +const { + constants: { BOLT_PROTOCOL_V5_3 } +} = internal + +export default class BoltProtocol extends BoltProtocolV5x2 { + get version () { + return BOLT_PROTOCOL_V5_3 + } + + get transformer () { + if (this._transformer === undefined) { + this._transformer = new Transformer(Object.values(transformersFactories).map(create => create(this._config, this._log))) + } + return this._transformer + } + + /** + * Initialize a connection with the server + * + * @param {Object} args The params + * @param {string} args.userAgent The user agent + * @param {any} args.authToken The auth token + * @param {NotificationFilter} args.notificationFilter The notification filters. + * @param {function(error)} args.onError On error callback + * @param {function(onComplete)} args.onComplete On complete callback + * @returns {LoginObserver} The Login observer + */ + initialize ({ userAgent, boltAgent, authToken, notificationFilter, onError, onComplete } = {}) { + const state = {} + const observer = new LoginObserver({ + onError: error => this._onLoginError(error, onError), + onCompleted: metadata => { + state.metadata = metadata + return this._onLoginCompleted(metadata) + } + }) + + this.write( + RequestMessage.hello5x3(userAgent, boltAgent, notificationFilter, this._serversideRouting), + observer, + false + ) + + return this.logon({ + authToken, + onComplete: metadata => onComplete({ ...metadata, ...state.metadata }), + onError, + flush: true + }) + } +} diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.transformer.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.transformer.js new file mode 100644 index 000000000..7af755ff3 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x3.transformer.js @@ -0,0 +1,24 @@ +/** + * 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. + */ + +import v5x2 from './bolt-protocol-v5x2.transformer.js' + +export default { + ...v5x2 +} diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js index 95cbbbb8a..0b0918662 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js @@ -29,6 +29,7 @@ import BoltProtocolV4x4 from './bolt-protocol-v4x4.js' import BoltProtocolV5x0 from './bolt-protocol-v5x0.js' import BoltProtocolV5x1 from './bolt-protocol-v5x1.js' import BoltProtocolV5x2 from './bolt-protocol-v5x2.js' +import BoltProtocolV5x3 from './bolt-protocol-v5x3.js' // eslint-disable-next-line no-unused-vars import { Chunker, Dechunker } from '../channel/index.js' import ResponseHandler from './response-handler.js' @@ -213,6 +214,14 @@ function createProtocol ( onProtocolError, serversideRouting ) + case 5.3: + return new BoltProtocolV5x3(server, + chunker, + packingConfig, + createResponseHandler, + log, + onProtocolError, + serversideRouting) default: throw newError('Unknown Bolt protocol version: ' + version) } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/handshake.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/handshake.js index ef7ff76f1..820eaf12c 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/handshake.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/handshake.js @@ -76,7 +76,7 @@ function parseNegotiatedResponse (buffer) { */ function newHandshakeBuffer () { return createHandshakeMessage([ - [version(5, 2), version(5, 0)], + [version(5, 3), version(5, 0)], [version(4, 4), version(4, 2)], version(4, 1), version(3, 0) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js index 574cdee80..6822f9c4f 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js @@ -191,6 +191,42 @@ export default class RequestMessage { ) } + /** + * Create a new HELLO message. + * @param {string} userAgent the user agent. + * @param {string} boltAgent the bolt agent. + * @param {NotificationFilter} notificationFilter the notification filter configured + * @param {Object} routing server side routing, set to routing context to turn on server side routing (> 4.1) + * @return {RequestMessage} new HELLO message. + */ + static hello5x3 (userAgent, boltAgent, notificationFilter = null, routing = null) { + const metadata = { bolt_agent: boltAgent } + + if (userAgent) { + metadata.user_agent = userAgent + } + + if (notificationFilter) { + if (notificationFilter.minimumSeverityLevel) { + metadata.notifications_minimum_severity = notificationFilter.minimumSeverityLevel + } + + if (notificationFilter.disabledCategories) { + metadata.notifications_disabled_categories = notificationFilter.disabledCategories + } + } + + if (routing) { + metadata.routing = routing + } + + return new RequestMessage( + HELLO, + [metadata], + () => `HELLO ${json.stringify(metadata)}` + ) + } + /** * Create a new LOGON message. * diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js index bc9ddb666..c12ac7211 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js @@ -37,8 +37,13 @@ const { const { SERVICE_UNAVAILABLE } = error export default class DirectConnectionProvider extends PooledConnectionProvider { +<<<<<<< HEAD constructor ({ id, config, log, address, userAgent, authTokenManager, newPool }) { super({ id, config, log, userAgent, authTokenManager, newPool }) +======= + constructor ({ id, config, log, address, userAgent, boltAgent, authToken }) { + super({ id, config, log, userAgent, boltAgent, authToken }) +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) this._address = address } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js index bd4e45a4a..223525b34 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js @@ -33,7 +33,11 @@ const AUTHENTICATION_ERRORS = [ export default class PooledConnectionProvider extends ConnectionProvider { constructor ( +<<<<<<< HEAD { id, config, log, userAgent, boltAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, +======= + { id, config, log, userAgent, boltAgent, authToken }, +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) createChannelConnectionHook = null ) { super() @@ -41,8 +45,14 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._id = id this._config = config this._log = log +<<<<<<< HEAD this._authTokenManager = authTokenManager this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent, boltAgent }) +======= + this._userAgent = userAgent + this._boltAgent = boltAgent + this._authToken = authToken +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) this._createChannelConnection = createChannelConnectionHook || (address => { @@ -85,7 +95,12 @@ export default class PooledConnectionProvider extends ConnectionProvider { return release(address, connection) } this._openConnections[connection.id] = connection +<<<<<<< HEAD return this._authenticationProvider.authenticate({ connection, auth }) +======= + return connection + .connect(this._userAgent, this._boltAgent, this._authToken) +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) .catch(error => { // let's destroy this connection this._destroyConnection(connection) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js index c66d09b97..216db9fbb 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js @@ -67,11 +67,19 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider config, log, userAgent, +<<<<<<< HEAD authTokenManager, routingTablePurgeDelay, newPool }) { super({ id, config, log, userAgent, authTokenManager, newPool }, address => { +======= + boltAgent, + authToken, + routingTablePurgeDelay + }) { + super({ id, config, log, userAgent, boltAgent, authToken }, address => { +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) return createChannelConnection( address, this._config, diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js index ea1b82d75..f6e8804a7 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js @@ -227,6 +227,7 @@ export default class ChannelConnection extends Connection { /** * Perform protocol-specific initialization which includes authentication. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if initialization is successful. Rejected promise otherwise. */ @@ -235,8 +236,8 @@ export default class ChannelConnection extends Connection { return new Promise((resolve, reject) => { this._protocol.initialize({ userAgent, - authToken, boltAgent, + authToken, notificationFilter: this._notificationFilter, onError: err => reject(err), onComplete: metadata => { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js index 70b43b2fa..ba302fe27 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js @@ -83,8 +83,13 @@ export default class DelegateConnection extends Connection { return this._delegate.protocol() } +<<<<<<< HEAD connect (userAgent, authToken, waitReAuth) { return this._delegate.connect(userAgent, authToken, waitReAuth) +======= + connect (userAgent, boltAgent, authToken) { + return this._delegate.connect(userAgent, boltAgent, authToken) +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) } write (message, observer, flush) { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js index d9856921d..a81b592fa 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js @@ -90,10 +90,11 @@ export default class Connection { /** * Connect to the target address, negotiate Bolt protocol and send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { + connect (userAgent, boltAgent, authToken) { throw new Error('not implemented') } diff --git a/packages/neo4j-driver-deno/lib/core/connection.ts b/packages/neo4j-driver-deno/lib/core/connection.ts index c97c873bb..8a7a18f3a 100644 --- a/packages/neo4j-driver-deno/lib/core/connection.ts +++ b/packages/neo4j-driver-deno/lib/core/connection.ts @@ -83,10 +83,11 @@ class Connection { /** * Connect to the target address, negotiate Bolt protocol and send initialization message. * @param {string} userAgent the user agent for this driver. + * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent: string, authToken: any): Promise { + connect (userAgent: string, boltAgent: string, authToken: any): Promise { throw Error('Not implemented') } diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts new file mode 100644 index 000000000..12febc2c9 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -0,0 +1,21 @@ +/** +* 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. +*/ +export function fromVersion (version: string): string { + return `neo4j-javascript/${version} (browser)}` +} diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/index.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/index.ts new file mode 100644 index 000000000..843139ce8 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './bolt-agent.ts' diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts new file mode 100644 index 000000000..0fe31ebb2 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts @@ -0,0 +1,21 @@ +/** +* 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. +*/ +export function fromVersion (version: string): string { + return `neo4j-javascript/${version} (browser)` +} diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/index.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/index.ts new file mode 100644 index 000000000..843139ce8 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './bolt-agent.ts' diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/index.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/index.ts new file mode 100644 index 000000000..9a11d26b6 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './deno/index.ts' diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts new file mode 100644 index 000000000..d639f1fce --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts @@ -0,0 +1,44 @@ +/** +* 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. +*/ +import * as os from 'os' + +export function fromVersion (version: string): string { + const HOST_ARCH = process.config.variables.host_arch + const NODE_VERSION = 'Node/' + process.versions.node + const NODE_V8_VERSION = process.versions.v8 + + const osName = mapOs(os.platform()) + + return `neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` +} + +export function mapOs (osType: string): string { + let osName + if (osType === 'darwin') { + osName = 'MacOS' + } else if (osType === 'win32') { + osName = 'Windows' + } else if (osType === 'linux') { + osName = 'Linux' + } else { + osName = osType + } + + return osName +} diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/index.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/index.ts new file mode 100644 index 000000000..843139ce8 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/index.ts @@ -0,0 +1,20 @@ +/** + * 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. + */ + +export * from './bolt-agent.ts' diff --git a/packages/neo4j-driver-deno/lib/core/internal/constants.ts b/packages/neo4j-driver-deno/lib/core/internal/constants.ts index d42d1da1c..2c56e9f1b 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/constants.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/constants.ts @@ -36,6 +36,7 @@ const BOLT_PROTOCOL_V4_4: number = 4.4 const BOLT_PROTOCOL_V5_0: number = 5.0 const BOLT_PROTOCOL_V5_1: number = 5.1 const BOLT_PROTOCOL_V5_2: number = 5.2 +const BOLT_PROTOCOL_V5_3: number = 5.3 export { FETCH_ALL, @@ -54,5 +55,6 @@ export { BOLT_PROTOCOL_V4_4, BOLT_PROTOCOL_V5_0, BOLT_PROTOCOL_V5_1, - BOLT_PROTOCOL_V5_2 + BOLT_PROTOCOL_V5_2, + BOLT_PROTOCOL_V5_3 } diff --git a/packages/neo4j-driver-deno/lib/core/internal/index.ts b/packages/neo4j-driver-deno/lib/core/internal/index.ts index a618369ad..a2a7530b7 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/index.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/index.ts @@ -30,6 +30,7 @@ import * as urlUtil from './url-util.ts' import * as serverAddress from './server-address.ts' import * as resolver from './resolver/index.ts' import * as objectUtil from './object-util.ts' +import * as boltAgent from './bolt-agent/index.ts' export { util, @@ -44,5 +45,6 @@ export { urlUtil, serverAddress, resolver, - objectUtil + objectUtil, + boltAgent } diff --git a/packages/neo4j-driver-deno/lib/core/types.ts b/packages/neo4j-driver-deno/lib/core/types.ts index 258ee38fd..c509145d3 100644 --- a/packages/neo4j-driver-deno/lib/core/types.ts +++ b/packages/neo4j-driver-deno/lib/core/types.ts @@ -64,7 +64,7 @@ export interface Config { useBigInt?: boolean logging?: LoggingConfig resolver?: (address: string) => string[] | Promise - userAgent?: string, + userAgent?: string boltAgent?: string } diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 2715877ce..442fe4819 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -17,93 +17,94 @@ * limitations under the License. */ import VERSION from './version.ts' -import { logging } from './logging.ts' +import {logging} from './logging.ts' import { - Neo4jError, - isRetriableError, + auth, + BookmarkManager, + bookmarkManager, + BookmarkManagerConfig, + Connection, + ConnectionProvider, + Date, + DateTime, + Driver, + driver as coreDriver, + Duration, + EagerResult, error, - Integer, inSafeRange, int, - isInt, - toNumber, - toString, + Integer, internal, - isPoint, - Point, - Date, - DateTime, - Duration, isDate, isDateTime, isDuration, + isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, + isPoint, isRelationship, + isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, - Time, + ManagedTransaction, + Neo4jError, Node, - Path, - PathSegment, - Relationship, - UnboundRelationship, - Record, - ResultSummary, - Result, - EagerResult, - ConnectionProvider, - Driver, - QueryResult, - ResultObserver, - Plan, - ProfiledPlan, - QueryStatistics, Notification, - NotificationPosition, - Session, - Transaction, - ManagedTransaction, - TransactionPromise, - ServerInfo, - Connection, - driver as coreDriver, - types as coreTypes, - auth, - BookmarkManager, - bookmarkManager, - BookmarkManagerConfig, - SessionConfig, - QueryConfig, - RoutingControl, - routing, - resultTransformers, - ResultTransformer, notificationCategory, - notificationSeverityLevel, - NotificationSeverityLevel, NotificationCategory, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel, notificationFilterDisabledCategory, +<<<<<<< HEAD notificationFilterMinimumSeverityLevel, AuthTokenManager, expirationBasedAuthTokenManager, AuthTokenAndExpiration, staticAuthTokenManager +======= + NotificationFilterMinimumSeverityLevel, + notificationFilterMinimumSeverityLevel, + NotificationPosition, + notificationSeverityLevel, + NotificationSeverityLevel, + Path, + PathSegment, + Plan, + Point, + ProfiledPlan, + QueryConfig, + QueryResult, + QueryStatistics, + Record, + Relationship, + Result, + ResultObserver, + ResultSummary, + ResultTransformer, + resultTransformers, + routing, + RoutingControl, + ServerInfo, + Session, + SessionConfig, + Time, + toNumber, + toString, + Transaction, + TransactionPromise, + types as coreTypes, + UnboundRelationship +>>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) } from './core/index.ts' // @deno-types=./bolt-connection/types/index.d.ts -import { - DirectConnectionProvider, - RoutingConnectionProvider -} from './bolt-connection/index.js' +import {DirectConnectionProvider, RoutingConnectionProvider} from './bolt-connection/index.js' type AuthToken = coreTypes.AuthToken type Config = coreTypes.Config @@ -336,7 +337,7 @@ function driver ( const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent ?? USER_AGENT + config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -363,6 +364,7 @@ function driver ( authTokenManager, address, userAgent: config.userAgent, + boltAgent: config.boltAgent, routingContext: parsedUrl.query }) } else { @@ -379,7 +381,8 @@ function driver ( log, authTokenManager, address, - userAgent: config.userAgent + userAgent: config.userAgent, + boltAgent: config.boltAgent }) } } @@ -405,8 +408,6 @@ async function hasReachableServer (url: string, config?: Pick } } -const USER_AGENT: string = 'neo4j-javascript/' + VERSION - /** * Object containing constructors for all neo4j types. */ diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index bc09e603c..a327f8270 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -20,89 +20,86 @@ import VERSION from './version' import { logging } from './logging' import { - Neo4jError, - isRetriableError, + auth, + BookmarkManager, + bookmarkManager, + BookmarkManagerConfig, + Connection, + ConnectionProvider, + Date, + DateTime, + Driver, + driver as coreDriver, + Duration, + EagerResult, error, - Integer, inSafeRange, int, - isInt, - toNumber, - toString, + Integer, internal, - isPoint, - Point, - Date, - DateTime, - Duration, isDate, isDateTime, isDuration, + isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, + isPoint, isRelationship, + isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, - Time, + ManagedTransaction, + Neo4jError, Node, - Path, - PathSegment, - Relationship, - UnboundRelationship, - Record, - ResultSummary, - Result, - EagerResult, - ConnectionProvider, - Driver, - QueryResult, - ResultObserver, - Plan, - ProfiledPlan, - QueryStatistics, Notification, - NotificationPosition, - Session, - Transaction, - ManagedTransaction, - TransactionPromise, - ServerInfo, - Connection, - driver as coreDriver, - types as coreTypes, - auth, - BookmarkManager, - bookmarkManager, - BookmarkManagerConfig, - SessionConfig, - QueryConfig, - RoutingControl, - routing, - resultTransformers, - ResultTransformer, notificationCategory, - notificationSeverityLevel, - NotificationSeverityLevel, NotificationCategory, NotificationFilter, NotificationFilterDisabledCategory, - NotificationFilterMinimumSeverityLevel, notificationFilterDisabledCategory, - notificationFilterMinimumSeverityLevel, AuthTokenManager, expirationBasedAuthTokenManager, AuthTokenAndExpiration, staticAuthTokenManager + NotificationFilterMinimumSeverityLevel, + notificationFilterMinimumSeverityLevel, + NotificationPosition, + notificationSeverityLevel, + NotificationSeverityLevel, + Path, + PathSegment, + Plan, + Point, + ProfiledPlan, + QueryConfig, + QueryResult, + QueryStatistics, + Record, + Relationship, + Result, + ResultObserver, + ResultSummary, + ResultTransformer, + resultTransformers, + routing, + RoutingControl, + ServerInfo, + Session, + SessionConfig, + Time, + toNumber, + toString, + Transaction, + TransactionPromise, + types as coreTypes, + UnboundRelationship } from 'neo4j-driver-core' -import { - DirectConnectionProvider, - RoutingConnectionProvider -} from 'neo4j-driver-bolt-connection' +import { DirectConnectionProvider, RoutingConnectionProvider } from 'neo4j-driver-bolt-connection' type AuthToken = coreTypes.AuthToken type Config = coreTypes.Config @@ -335,8 +332,7 @@ function driver ( const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent ?? BOLT_AGENT - config.boltAgent = BOLT_AGENT + config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -407,8 +403,6 @@ async function hasReachableServer (url: string, config?: Pick } } -const BOLT_AGENT = internal.boltAgent.BoltAgent.fromVersion('neo4j-javascript/' + VERSION) - /** * Object containing constructors for all neo4j types. */ diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 3997e3f00..6ba8d557e 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -298,7 +298,7 @@ function driver (url, authToken, config = {}) { const authTokenManager = createAuthManager(authToken) - config.boltAgent = BOLT_AGENT + config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -364,8 +364,6 @@ async function hasReachableServer (url, config) { } } -const BOLT_AGENT = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) - /** * Object containing predefined logging configurations. These are expected to be used as values of the driver config's `logging` property. * @property {function(level: ?string): object} console the function to create a logging config that prints all messages to `console.log` with From c1f9f3b6bb5f9147608a976fbd4844e25de009ff Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Thu, 6 Apr 2023 12:01:27 +0100 Subject: [PATCH 05/18] changes made after rebase onto re-auth --- .../authentication-provider.js | 2 +- .../src/connection/connection-channel.js | 1 + .../src/connection/connection-delegate.js | 2 +- .../src/connection/connection.js | 3 +- .../bolt-protocol-v5x3.test.js.snap | 61 +++++++++++++++++++ .../test/bolt/bolt-protocol-v5x3.test.js | 2 +- .../authentication-provider.js | 2 +- .../connection-provider-direct.js | 9 +-- .../connection-provider-pooled.js | 14 +---- .../connection-provider-routing.js | 11 +--- .../connection/connection-channel.js | 10 +-- .../connection/connection-delegate.js | 9 +-- .../bolt-connection/connection/connection.js | 3 +- .../neo4j-driver-deno/lib/core/connection.ts | 3 +- packages/neo4j-driver-deno/lib/mod.ts | 10 +-- packages/neo4j-driver-lite/src/index.ts | 2 +- 16 files changed, 86 insertions(+), 58 deletions(-) create mode 100644 packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x3.test.js.snap diff --git a/packages/bolt-connection/src/connection-provider/authentication-provider.js b/packages/bolt-connection/src/connection-provider/authentication-provider.js index c69d21cff..77d06e11d 100644 --- a/packages/bolt-connection/src/connection-provider/authentication-provider.js +++ b/packages/bolt-connection/src/connection-provider/authentication-provider.js @@ -47,7 +47,7 @@ export default class AuthenticationProvider { const authToken = await this._authTokenManager.getToken() if (!object.equals(authToken, connection.authToken)) { - return await connection.connect(this._userAgent, authToken) + return await connection.connect(this._userAgent, this._boltAgent, authToken, false) } return connection diff --git a/packages/bolt-connection/src/connection/connection-channel.js b/packages/bolt-connection/src/connection/connection-channel.js index 53f1ea668..10691f5e9 100644 --- a/packages/bolt-connection/src/connection/connection-channel.js +++ b/packages/bolt-connection/src/connection/connection-channel.js @@ -185,6 +185,7 @@ export default class ChannelConnection extends Connection { * @param {string} userAgent the user agent for this driver. * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {boolean} waitReAuth whether ot not the connection will wait for re-authentication to happen * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ async connect (userAgent, boltAgent, authToken, waitReAuth) { diff --git a/packages/bolt-connection/src/connection/connection-delegate.js b/packages/bolt-connection/src/connection/connection-delegate.js index 8848408d3..31e2ade56 100644 --- a/packages/bolt-connection/src/connection/connection-delegate.js +++ b/packages/bolt-connection/src/connection/connection-delegate.js @@ -84,7 +84,7 @@ export default class DelegateConnection extends Connection { } connect (userAgent, boltAgent, authToken, waitReAuth) { - return this._delegate.connect(userAgent, authToken, boltAgent, waitReAuth) + return this._delegate.connect(userAgent, boltAgent, authToken, waitReAuth) } write (message, observer, flush) { diff --git a/packages/bolt-connection/src/connection/connection.js b/packages/bolt-connection/src/connection/connection.js index c0e1dc947..463c52b8f 100644 --- a/packages/bolt-connection/src/connection/connection.js +++ b/packages/bolt-connection/src/connection/connection.js @@ -92,9 +92,10 @@ export default class Connection { * @param {string} userAgent the user agent for this driver. * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {boolean} shouldWaitReAuth whether ot not the connection will wait for re-authentication to happen * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, boltAgent, authToken) { + connect (userAgent, boltAgent, authToken, shouldWaitReAuth) { throw new Error('not implemented') } diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x3.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x3.test.js.snap new file mode 100644 index 000000000..b9c718365 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x3.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV5x3 .packable() should pack not pack graph types (Node) 1`] = `"It is not allowed to pass nodes in query parameters, given: (c:a {a:\\"b\\"})"`; + +exports[`#unit BoltProtocolV5x3 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV5x3 .packable() should pack not pack graph types (Relationship) 1`] = `"It is not allowed to pass relationships in query parameters, given: (e)-[:a {b:\\"c\\"}]->(f)"`; + +exports[`#unit BoltProtocolV5x3 .packable() should pack not pack graph types (UnboundRelationship) 1`] = `"It is not allowed to pass unbound relationships in query parameters, given: -[:a {b:\\"c\\"}]->"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 5"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 9"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x3 .unpack() should not unpack with wrong size (UnboundRelationship with more fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 4 but was 5"`; diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js index 6be3593e7..5d7dc56f5 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js @@ -245,7 +245,7 @@ describe('#unit BoltProtocolV5x3', () => { it('should return correct bolt version number', () => { const protocol = new BoltProtocolV5x3(null, null, false) - expect(protocol.version).toBe(5.2) + expect(protocol.version).toBe(5.3) }) it('should update metadata', () => { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js index 8467afa93..30e5fa92b 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/authentication-provider.js @@ -47,7 +47,7 @@ export default class AuthenticationProvider { const authToken = await this._authTokenManager.getToken() if (!object.equals(authToken, connection.authToken)) { - return await connection.connect(this._userAgent, authToken) + return await connection.connect(this._userAgent, this._boltAgent, authToken, false) } return connection diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js index c12ac7211..46bb5c0e6 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-direct.js @@ -37,13 +37,8 @@ const { const { SERVICE_UNAVAILABLE } = error export default class DirectConnectionProvider extends PooledConnectionProvider { -<<<<<<< HEAD - constructor ({ id, config, log, address, userAgent, authTokenManager, newPool }) { - super({ id, config, log, userAgent, authTokenManager, newPool }) -======= - constructor ({ id, config, log, address, userAgent, boltAgent, authToken }) { - super({ id, config, log, userAgent, boltAgent, authToken }) ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) + constructor ({ id, config, log, address, userAgent, boltAgent, authTokenManager, newPool }) { + super({ id, config, log, userAgent, boltAgent, authTokenManager, newPool }) this._address = address } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js index 223525b34..5e4cadd21 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-pooled.js @@ -33,11 +33,7 @@ const AUTHENTICATION_ERRORS = [ export default class PooledConnectionProvider extends ConnectionProvider { constructor ( -<<<<<<< HEAD { id, config, log, userAgent, boltAgent, authTokenManager, newPool = (...args) => new Pool(...args) }, -======= - { id, config, log, userAgent, boltAgent, authToken }, ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) createChannelConnectionHook = null ) { super() @@ -45,14 +41,10 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._id = id this._config = config this._log = log -<<<<<<< HEAD this._authTokenManager = authTokenManager this._authenticationProvider = new AuthenticationProvider({ authTokenManager, userAgent, boltAgent }) -======= this._userAgent = userAgent this._boltAgent = boltAgent - this._authToken = authToken ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) this._createChannelConnection = createChannelConnectionHook || (address => { @@ -95,12 +87,8 @@ export default class PooledConnectionProvider extends ConnectionProvider { return release(address, connection) } this._openConnections[connection.id] = connection -<<<<<<< HEAD + return this._authenticationProvider.authenticate({ connection, auth }) -======= - return connection - .connect(this._userAgent, this._boltAgent, this._authToken) ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) .catch(error => { // let's destroy this connection this._destroyConnection(connection) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js index 216db9fbb..804d64b49 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection-provider/connection-provider-routing.js @@ -67,19 +67,12 @@ export default class RoutingConnectionProvider extends PooledConnectionProvider config, log, userAgent, -<<<<<<< HEAD + boltAgent, authTokenManager, routingTablePurgeDelay, newPool }) { - super({ id, config, log, userAgent, authTokenManager, newPool }, address => { -======= - boltAgent, - authToken, - routingTablePurgeDelay - }) { - super({ id, config, log, userAgent, boltAgent, authToken }, address => { ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) + super({ id, config, log, userAgent, boltAgent, authTokenManager, newPool }, address => { return createChannelConnection( address, this._config, diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js index f6e8804a7..df21b3c59 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-channel.js @@ -185,10 +185,10 @@ export default class ChannelConnection extends Connection { * @param {string} userAgent the user agent for this driver. * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {boolean} waitReAuth whether ot not the connection will wait for re-authentication to happen * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ -<<<<<<< HEAD - async connect (userAgent, authToken, waitReAuth) { + async connect (userAgent, boltAgent, authToken, waitReAuth) { if (this._protocol.initialized && !this._protocol.supportsReAuth) { throw newError('Connection does not support re-auth') } @@ -196,7 +196,7 @@ export default class ChannelConnection extends Connection { this._authToken = authToken if (!this._protocol.initialized) { - return await this._initialize(userAgent, authToken) + return await this._initialize(userAgent, boltAgent, authToken) } if (waitReAuth) { @@ -218,10 +218,6 @@ export default class ChannelConnection extends Connection { this._protocol.logon({ authToken, flush: true }) return this -======= - connect (userAgent, boltAgent, authToken) { - return this._initialize(userAgent, boltAgent, authToken) ->>>>>>> dce70ae8 (Bolt agent added to hello metadata) } /** diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js index ba302fe27..a35307e69 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection-delegate.js @@ -83,13 +83,8 @@ export default class DelegateConnection extends Connection { return this._delegate.protocol() } -<<<<<<< HEAD - connect (userAgent, authToken, waitReAuth) { - return this._delegate.connect(userAgent, authToken, waitReAuth) -======= - connect (userAgent, boltAgent, authToken) { - return this._delegate.connect(userAgent, boltAgent, authToken) ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) + connect (userAgent, boltAgent, authToken, waitReAuth) { + return this._delegate.connect(userAgent, boltAgent, authToken, waitReAuth) } write (message, observer, flush) { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js index a81b592fa..0913951fa 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js @@ -92,9 +92,10 @@ export default class Connection { * @param {string} userAgent the user agent for this driver. * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {boolean} shouldWaitReAuth whether ot not the connection will wait for re-authentication to happen * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, boltAgent, authToken) { + connect (userAgent, boltAgent, authToken, shouldWaitReAuth) { throw new Error('not implemented') } diff --git a/packages/neo4j-driver-deno/lib/core/connection.ts b/packages/neo4j-driver-deno/lib/core/connection.ts index 8a7a18f3a..9ac5950ce 100644 --- a/packages/neo4j-driver-deno/lib/core/connection.ts +++ b/packages/neo4j-driver-deno/lib/core/connection.ts @@ -85,9 +85,10 @@ class Connection { * @param {string} userAgent the user agent for this driver. * @param {string} boltAgent the bolt agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {Object} waitReAuth whether to connect method should wait until re-Authorised * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent: string, boltAgent: string, authToken: any): Promise { + connect (userAgent: string, boltAgent: string, authToken: any, waitReAuth: false): Promise { throw Error('Not implemented') } diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 442fe4819..9421e9dca 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -17,7 +17,7 @@ * limitations under the License. */ import VERSION from './version.ts' -import {logging} from './logging.ts' +import { logging } from './logging.ts' import { auth, @@ -62,13 +62,10 @@ import { NotificationFilter, NotificationFilterDisabledCategory, notificationFilterDisabledCategory, -<<<<<<< HEAD - notificationFilterMinimumSeverityLevel, AuthTokenManager, expirationBasedAuthTokenManager, AuthTokenAndExpiration, - staticAuthTokenManager -======= + staticAuthTokenManager, NotificationFilterMinimumSeverityLevel, notificationFilterMinimumSeverityLevel, NotificationPosition, @@ -101,10 +98,9 @@ import { TransactionPromise, types as coreTypes, UnboundRelationship ->>>>>>> 0b8c1b40 (Fix useragent string on metadata object in 5.3) } from './core/index.ts' // @deno-types=./bolt-connection/types/index.d.ts -import {DirectConnectionProvider, RoutingConnectionProvider} from './bolt-connection/index.js' +import { DirectConnectionProvider, RoutingConnectionProvider } from './bolt-connection/index.js' type AuthToken = coreTypes.AuthToken type Config = coreTypes.Config diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index a327f8270..8542e9062 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -65,7 +65,7 @@ import { AuthTokenManager, expirationBasedAuthTokenManager, AuthTokenAndExpiration, - staticAuthTokenManager + staticAuthTokenManager, NotificationFilterMinimumSeverityLevel, notificationFilterMinimumSeverityLevel, NotificationPosition, From 75d73cc8021ebef08b44955e17c41189355af1da Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Tue, 11 Apr 2023 09:20:17 +0100 Subject: [PATCH 06/18] fixed deno bolt agent --- .../test/bolt/bolt-protocol-v5x2.test.js | 33 +++++++++++++++++++ .../internal/bolt-agent/browser/bolt-agent.ts | 10 +++++- .../internal/bolt-agent/deno/bolt-agent.ts | 10 +++++- .../internal/bolt-agent/node/bolt-agent.ts | 20 ++--------- packages/core/tsconfig.build.json | 3 ++ packages/core/tsconfig.json | 2 +- packages/neo4j-driver-deno/generate.ts | 4 --- .../internal/bolt-agent/browser/bolt-agent.ts | 2 ++ .../internal/bolt-agent/deno/bolt-agent.ts | 8 ++++- .../internal/bolt-agent/node/bolt-agent.ts | 22 ++----------- 10 files changed, 69 insertions(+), 45 deletions(-) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js index d4a1e10da..b55cd31c0 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js @@ -295,6 +295,39 @@ describe('#unit BoltProtocolV5x2', () => { expect(protocol.flushes).toEqual([false, true]) }) + it('should set userAgent to bolt agent when userAgent is null', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x2(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent: null, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(2) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello5x1(clientName) + ) + expect(protocol.messages[1]).toBeMessage( + RequestMessage.logon(authToken) + ) + + expect(protocol.observers.length).toBe(2) + + // hello observer + const helloObserver = protocol.observers[0] + expect(helloObserver).toBeInstanceOf(LoginObserver) + expect(helloObserver).not.toBe(observer) + + // login observer + const loginObserver = protocol.observers[1] + expect(loginObserver).toBeInstanceOf(LoginObserver) + expect(loginObserver).toBe(observer) + + expect(protocol.flushes).toEqual([false, true]) + }) + it.each( [true, false] )('should logon to the server [flush=%s]', (flush) => { diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index 12febc2c9..83622ec91 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -16,6 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* eslint-disable */ export function fromVersion (version: string): string { - return `neo4j-javascript/${version} (browser)}` + // @ts-ignore: browser code so must be skipped by ts + const APP_VERSION = window.navigator.appVersion + + //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 + const OS = APP_VERSION.split(" ")[0]; + + return `neo4j-javascript/${version} ${OS} ` } +/* eslint-enable */ diff --git a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts index 0fe31ebb2..e5b58a7d4 100644 --- a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts @@ -16,6 +16,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +/* eslint-disable */ export function fromVersion (version: string): string { - return `neo4j-javascript/${version} (browser)` + const HOST_ARCH = Deno.build.arch + const DENO_VERSION = `Deno/${Deno.version.deno}` + const NODE_V8_VERSION = Deno.version.v8 + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease}` + + return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } +/* eslint-enable */ diff --git a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts index d639f1fce..fe76a0a95 100644 --- a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts @@ -22,23 +22,7 @@ export function fromVersion (version: string): string { const HOST_ARCH = process.config.variables.host_arch const NODE_VERSION = 'Node/' + process.versions.node const NODE_V8_VERSION = process.versions.v8 + const OS_NAME_VERSION = `${os.platform()} ${os.release()}` - const osName = mapOs(os.platform()) - - return `neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` -} - -export function mapOs (osType: string): string { - let osName - if (osType === 'darwin') { - osName = 'MacOS' - } else if (osType === 'win32') { - osName = 'Windows' - } else if (osType === 'linux') { - osName = 'Linux' - } else { - osName = osType - } - - return osName + return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` } diff --git a/packages/core/tsconfig.build.json b/packages/core/tsconfig.build.json index ca9a9cbe0..a42402bff 100644 --- a/packages/core/tsconfig.build.json +++ b/packages/core/tsconfig.build.json @@ -2,5 +2,8 @@ "extends": "./tsconfig.json", "include": [ "src/**/*.ts", + ], + "exclude": [ + "src/internal/bolt-agent/deno/*.ts" ] } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 382a043dd..c7090d357 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -16,5 +16,5 @@ "include": [ "src/**/*.ts", "test/**/*.ts" - ] + ], } diff --git a/packages/neo4j-driver-deno/generate.ts b/packages/neo4j-driver-deno/generate.ts index dc11f9579..02d3c0565 100644 --- a/packages/neo4j-driver-deno/generate.ts +++ b/packages/neo4j-driver-deno/generate.ts @@ -132,8 +132,6 @@ async function copyAndTransform(inDir: string, outDir: string) { // Special fix for core/internal/bolt-agent/index.js // Replace the "node boltAgent" with the "deno boltAgent", since Deno supports different APIs if(inPath.endsWith("bolt-agent/index.ts")){ - console.log(inPath) - console.log(contents) contents = contents.replace( `export * from './node/index.ts'`, `export * from './deno/index.ts'`, @@ -143,8 +141,6 @@ async function copyAndTransform(inDir: string, outDir: string) { // Special fix for bolt-connection/channel/index.js and core/internal/bolt-agent/index.js // Replace the "node channel" with the "deno channel", since Deno supports different APIs if (inPath.endsWith("channel/index.js")) { - console.log(inPath) - console.log(contents) contents = contents.replace( `export * from './node/index.js'`, `export * from './deno/index.js'`, diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts index 12febc2c9..351e316cf 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -17,5 +17,7 @@ * limitations under the License. */ export function fromVersion (version: string): string { + window.navagator + return `neo4j-javascript/${version} (browser)}` } diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts index 0fe31ebb2..58416d878 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts @@ -16,6 +16,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + export function fromVersion (version: string): string { - return `neo4j-javascript/${version} (browser)` + const HOST_ARCH = Deno.build.arch + const DENO_VERSION = 'Deno/' + Deno.version.deno + const NODE_V8_VERSION = Deno.version.v8 + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease}` + + return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts index d639f1fce..099306a70 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts @@ -22,23 +22,7 @@ export function fromVersion (version: string): string { const HOST_ARCH = process.config.variables.host_arch const NODE_VERSION = 'Node/' + process.versions.node const NODE_V8_VERSION = process.versions.v8 + const OS_NAME_VERSION = `${os.platform()} ${os.release()}` - const osName = mapOs(os.platform()) - - return `neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` -} - -export function mapOs (osType: string): string { - let osName - if (osType === 'darwin') { - osName = 'MacOS' - } else if (osType === 'win32') { - osName = 'Windows' - } else if (osType === 'linux') { - osName = 'Linux' - } else { - osName = osType - } - - return osName -} + return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` +} \ No newline at end of file From a0d059dd3a7d5d64e71801860092c68a82ded8cf Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Tue, 11 Apr 2023 11:12:30 +0100 Subject: [PATCH 07/18] Browser node agent --- packages/core/src/internal/bolt-agent/browser/bolt-agent.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index 83622ec91..de3cf870f 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -22,8 +22,8 @@ export function fromVersion (version: string): string { const APP_VERSION = window.navigator.appVersion //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = APP_VERSION.split(" ")[0]; + const OS = APP_VERSION.split("(")[1].split(")")[0]; - return `neo4j-javascript/${version} ${OS} ` + return `neo4j-javascript/${version} (${OS})` } /* eslint-enable */ From ee556a679f86cbaab7661a81552aceba91f4c119 Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Tue, 11 Apr 2023 12:43:21 +0100 Subject: [PATCH 08/18] browser bolt agent test --- .../internal/bolt-agent/browser/bolt-agent.ts | 5 ++- .../internal/bolt-agent/deno/bolt-agent.ts | 4 ++ .../bolt-agent/browser/bolt-agent.test.ts | 38 +++++++++++++++++++ .../bolt-agent/node/bolt-agent.test.ts | 2 +- 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index de3cf870f..4e2c97390 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -17,9 +17,10 @@ * limitations under the License. */ /* eslint-disable */ -export function fromVersion (version: string): string { +// @ts-ignore: browser code so must be skipped by ts +export function fromVersion (version: string, windowProvider = () => window): string { // @ts-ignore: browser code so must be skipped by ts - const APP_VERSION = window.navigator.appVersion + const APP_VERSION = windowProvider().navigator.appVersion //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 const OS = APP_VERSION.split("(")[1].split(")")[0]; diff --git a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts index e5b58a7d4..1efa3cc55 100644 --- a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts @@ -19,9 +19,13 @@ /* eslint-disable */ export function fromVersion (version: string): string { + //@ts-ignore const HOST_ARCH = Deno.build.arch + //@ts-ignore const DENO_VERSION = `Deno/${Deno.version.deno}` + //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 + //@ts-ignore const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease}` return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` diff --git a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts new file mode 100644 index 000000000..210ffac61 --- /dev/null +++ b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts @@ -0,0 +1,38 @@ +/** + * 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. + */ +import { fromVersion } from '../../../../src/internal/bolt-agent/browser' + +describe('#unit boltAgent', () => { + // This test is very fragile but the exact look of this string should not change without PM approval + it('should return the correct bolt agent for specified version', () => { + const version = '5.3' + const windowProvider = (): any => { + return { + navigator: { + appVersion: '5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' + } + } + } + + const boltAgent = fromVersion(version, windowProvider) + + expect(boltAgent.length === 0).toBeFalsy() + expect(boltAgent).toEqual(`neo4j-javascript/${version} (Macintosh; Intel Mac OS X 10_15_7)`) + }) +}) diff --git a/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts index dd2094c02..5804202ad 100644 --- a/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts +++ b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts @@ -29,7 +29,7 @@ describe('#unit boltAgent', () => { const NODE_VERSION = 'Node/' + process.versions.node const NODE_V8_VERSION = process.versions.v8 - const osName = BoltAgent.mapOs(os.platform()) + const osName = os.platform() expect(boltAgent.length === 0).toBeFalsy() expect(boltAgent).toContain(`neo4j-javascript/${version}`) From eb9897a64da67bf1697099deae61e15b9f3cef1c Mon Sep 17 00:00:00 2001 From: Conor Watson Date: Tue, 11 Apr 2023 15:50:52 +0100 Subject: [PATCH 09/18] Add deno test --- packages/core/package.json | 2 +- .../internal/bolt-agent/deno/bolt-agent.ts | 2 +- .../bolt-agent/deno/bolt-agent.test.ts | 47 +++++++++++++++++++ .../internal/bolt-agent/browser/bolt-agent.ts | 13 +++-- .../internal/bolt-agent/deno/bolt-agent.ts | 10 +++- .../internal/bolt-agent/node/bolt-agent.ts | 2 +- 6 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts diff --git a/packages/core/package.json b/packages/core/package.json index 62f20efec..64e761ba6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,7 +10,7 @@ "test": "jest", "test::watch": "jest --watch", "test::unit": "npm run test", - "test::deno": "deno test --allow-none ./test/deno/", + "test::deno": "deno test --allow-sys ./test/deno/", "predocs": "npm run build && npm run build::es6", "docs": "esdoc -c esdoc.json", "prepare": "npm run build", diff --git a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts index 1efa3cc55..ac06f4537 100644 --- a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts @@ -26,7 +26,7 @@ export function fromVersion (version: string): string { //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease}` + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } diff --git a/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts new file mode 100644 index 000000000..dc8a969b2 --- /dev/null +++ b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts @@ -0,0 +1,47 @@ +/** + * 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. + */ +/* eslint-disable */ +//@ts-ignore +import { fromVersion } from '../../../../../src/internal/bolt-agent/deno/bolt-agent.ts' +//@ts-ignore +import { assertMatch, assertEquals } from "https://deno.land/std@0.182.0/testing/asserts.ts"; + +//@ts-ignore +Deno.test('Test Contains deno and javascript', () => { + const boltAgent = fromVersion("5.3") + assertMatch(boltAgent, new RegExp("^neo4j-javascript/5.3")) + assertMatch(boltAgent, new RegExp("Deno/")) +}); + +//@ts-ignore +Deno.test('Test full bolt agent', () => { + //@ts-ignore + const HOST_ARCH = Deno.build.arch + //@ts-ignore + const DENO_VERSION = `Deno/${Deno.version.deno}` + //@ts-ignore + const NODE_V8_VERSION = Deno.version.v8 + //@ts-ignore + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` + + const boltAgentExpected = `neo4j-javascript/5.3 (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` + + assertEquals(fromVersion("5.3"), boltAgentExpected) +}); +/* eslint-enable */ diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts index 351e316cf..4e2c97390 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -16,8 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export function fromVersion (version: string): string { - window.navagator +/* eslint-disable */ +// @ts-ignore: browser code so must be skipped by ts +export function fromVersion (version: string, windowProvider = () => window): string { + // @ts-ignore: browser code so must be skipped by ts + const APP_VERSION = windowProvider().navigator.appVersion - return `neo4j-javascript/${version} (browser)}` + //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 + const OS = APP_VERSION.split("(")[1].split(")")[0]; + + return `neo4j-javascript/${version} (${OS})` } +/* eslint-enable */ diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts index 58416d878..ac06f4537 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts @@ -17,11 +17,17 @@ * limitations under the License. */ +/* eslint-disable */ export function fromVersion (version: string): string { + //@ts-ignore const HOST_ARCH = Deno.build.arch - const DENO_VERSION = 'Deno/' + Deno.version.deno + //@ts-ignore + const DENO_VERSION = `Deno/${Deno.version.deno}` + //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease}` + //@ts-ignore + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } +/* eslint-enable */ diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts index 099306a70..fe76a0a95 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts @@ -25,4 +25,4 @@ export function fromVersion (version: string): string { const OS_NAME_VERSION = `${os.platform()} ${os.release()}` return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` -} \ No newline at end of file +} From ac379bee9d26a6b3c4a247f60f44aa91b901eb5b Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 16 May 2023 17:28:05 +0200 Subject: [PATCH 10/18] Fix unit/integration tests related with connection.connect and bolt-agent for Deno The test code related with `connection.connect` were calling the method without the bolt agent causing all kind of errors. Add the bolt agent to the calls and expectations solves the issue. `Deno.osRelease()` and the flag `--allow-sys` are not available in Deno 1.19.2. The missing flag was causing the test don't even start. The method unexistance was causing the code fails because is calling a non existing method. The missing flag is solved by using `--allow-all` instead. This is a more permissive flag, but it is not a big deal in test env. The absence of `Deno.osRelease()` is solved by not setting the release when this method is not available. This might be an issue for old Deno users, but it's not a blocker. --- .../authentication-provider.test.js | 28 ++++++++++--------- .../connection/connection-channel.test.js | 20 ++++++------- packages/core/package.json | 2 +- .../internal/bolt-agent/deno/bolt-agent.ts | 2 +- .../bolt-agent/deno/bolt-agent.test.ts | 2 +- .../internal/bolt-agent/deno/bolt-agent.ts | 2 +- .../test/internal/connection-channel.test.js | 22 +++++++-------- .../test/internal/connection-delegate.test.js | 2 +- 8 files changed, 41 insertions(+), 39 deletions(-) diff --git a/packages/bolt-connection/test/connection-provider/authentication-provider.test.js b/packages/bolt-connection/test/connection-provider/authentication-provider.test.js index 99dc57600..0b0a70ae2 100644 --- a/packages/bolt-connection/test/connection-provider/authentication-provider.test.js +++ b/packages/bolt-connection/test/connection-provider/authentication-provider.test.js @@ -21,6 +21,7 @@ import AuthenticationProvider from '../../src/connection-provider/authentication describe('AuthenticationProvider', () => { const USER_AGENT = 'javascript-driver/5.5.0' + const BOLT_AGENT = 'javascript-driver/5.5.0 some information about system' describe('.authenticate()', () => { describe('when called without an auth', () => { @@ -69,7 +70,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, authToken) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, authToken, false) }) it('should throw errors happened during token refresh', async () => { @@ -201,7 +202,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, authToken) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, authToken, false) }) it('should throw errors happened during token refresh', async () => { @@ -330,7 +331,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, authToken) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, authToken, false) }) it('should throw errors happened during connection.connect', async () => { @@ -405,7 +406,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, auth, false) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth, false) }) it('should return the connection', async () => { @@ -451,7 +452,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth }) - expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, auth) + expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth) }) it('should not call connection connect with the supplied auth and skipReAuth=true', async () => { @@ -462,7 +463,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth, skipReAuth: true }) - expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, auth) + expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth) }) if (supportsReAuth) { @@ -474,7 +475,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth, forceReAuth: true }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, auth, false) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth, false) }) } else { it('should not call connection connect with the supplied auth if forceReAuth=true', async () => { @@ -485,7 +486,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth, forceReAuth: true }) - expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, auth) + expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth) }) } @@ -519,7 +520,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, auth, false) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth, false) }) it('should return the connection', async () => { @@ -569,7 +570,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth, waitReAuth }) - expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, auth, expectedWaitForReAuth) + expect(connection.connect).toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth, expectedWaitForReAuth) }) it('should not call connect when skipReAuth=true', async () => { @@ -593,7 +594,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth }) - expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, auth) + expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth) }) it('should not call connection connect with the supplied auth and forceReAuth=true', async () => { @@ -604,7 +605,7 @@ describe('AuthenticationProvider', () => { await authenticationProvider.authenticate({ connection, auth, forceReAuth: true }) - expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, auth) + expect(connection.connect).not.toHaveBeenCalledWith(USER_AGENT, BOLT_AGENT, auth) }) it('should return the connection', async () => { @@ -787,7 +788,8 @@ describe('AuthenticationProvider', () => { const authTokenManager = expirationBasedAuthTokenManager({ tokenProvider: authTokenProvider }) const provider = new AuthenticationProvider({ authTokenManager, - userAgent: USER_AGENT + userAgent: USER_AGENT, + boltAgent: BOLT_AGENT }) if (mocks) { diff --git a/packages/bolt-connection/test/connection/connection-channel.test.js b/packages/bolt-connection/test/connection/connection-channel.test.js index 446747647..fb89e882e 100644 --- a/packages/bolt-connection/test/connection/connection-channel.test.js +++ b/packages/bolt-connection/test/connection/connection-channel.test.js @@ -48,7 +48,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ channel, protocolSupplier }) - await connection.connect('userAgent', {}) + await connection.connect('userAgent', 'boltAgent', {}) expect(channel.setupReceiveTimeout).toHaveBeenCalledWith(expected) } @@ -79,7 +79,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ channel, protocolSupplier }) - await connection.connect('userAgent', {}) + await connection.connect('userAgent', 'boltAgent', {}) expect(channel.setupReceiveTimeout).not.toHaveBeenCalled() } @@ -114,7 +114,7 @@ describe('ChannelConnection', () => { address }) - await connection.connect('userAgent', {}) + await connection.connect('userAgent', 'boltAgent', {}) expect(loggerFunction).toHaveBeenCalledWith( 'info', `Connection [${ @@ -142,7 +142,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ channel, protocolSupplier, notificationFilter }) - await connection.connect('userAgent', {}) + await connection.connect('userAgent', 'boltAgent', {}) const call = protocol.initialize.mock.calls[0][0] @@ -159,7 +159,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - await connection.connect('userAgent', authToken) + await connection.connect('userAgent', 'boltAgent', authToken) expect(connection.authToken).toEqual(authToken) }) @@ -181,7 +181,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - await connection.connect('userAgent', authToken) + await connection.connect('userAgent', 'boltAgent', authToken) expect(protocol.initialize).not.toHaveBeenCalled() expect(protocol.logoff).toHaveBeenCalledWith() @@ -207,7 +207,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - const connectionPromise = connection.connect('userAgent', authToken, true) + const connectionPromise = connection.connect('userAgent', 'boltAgent', authToken, true) const isPending = await Promise.race([connectionPromise, Promise.resolve(true)]) expect(isPending).toEqual(true) @@ -243,7 +243,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - const connectionPromise = connection.connect('userAgent', authToken, true) + const connectionPromise = connection.connect('userAgent', 'boltAgent', authToken, true) const isPending = await Promise.race([connectionPromise, Promise.resolve(true)]) expect(isPending).toEqual(true) @@ -278,7 +278,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - const connectionPromise = connection.connect('userAgent', authToken, true) + const connectionPromise = connection.connect('userAgent', 'boltAgent', authToken, true) const isPending = await Promise.race([connectionPromise, Promise.resolve(true)]) expect(isPending).toEqual(true) @@ -314,7 +314,7 @@ describe('ChannelConnection', () => { const protocolSupplier = () => protocol const connection = spyOnConnectionChannel({ protocolSupplier }) - await expect(connection.connect('userAgent', authToken)).rejects.toThrow( + await expect(connection.connect('userAgent', 'boltAgent', authToken)).rejects.toThrow( newError('Connection does not support re-auth') ) diff --git a/packages/core/package.json b/packages/core/package.json index 64e761ba6..c2ab729b1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -10,7 +10,7 @@ "test": "jest", "test::watch": "jest --watch", "test::unit": "npm run test", - "test::deno": "deno test --allow-sys ./test/deno/", + "test::deno": "deno test --allow-all ./test/deno/", "predocs": "npm run build && npm run build::es6", "docs": "esdoc -c esdoc.json", "prepare": "npm run build", diff --git a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts index ac06f4537..d2c42b785 100644 --- a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts @@ -26,7 +26,7 @@ export function fromVersion (version: string): string { //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } diff --git a/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts index dc8a969b2..20929339a 100644 --- a/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts +++ b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts @@ -38,7 +38,7 @@ Deno.test('Test full bolt agent', () => { //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() const boltAgentExpected = `neo4j-javascript/5.3 (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts index ac06f4537..d2c42b785 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts @@ -26,7 +26,7 @@ export function fromVersion (version: string): string { //@ts-ignore const NODE_V8_VERSION = Deno.version.v8 //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease()}` + const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` } diff --git a/packages/neo4j-driver/test/internal/connection-channel.test.js b/packages/neo4j-driver/test/internal/connection-channel.test.js index 714f7b437..33cb7a6db 100644 --- a/packages/neo4j-driver/test/internal/connection-channel.test.js +++ b/packages/neo4j-driver/test/internal/connection-channel.test.js @@ -104,7 +104,7 @@ describe('#integration ChannelConnection', () => { } connection - .connect('mydriver/0.0.0', basicAuthToken()) + .connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(() => { connection .protocol() @@ -177,7 +177,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) connection - .connect('mydriver/0.0.0', basicAuthToken()) + .connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(initializedConnection => { expect(initializedConnection).toBe(connection) done() @@ -189,7 +189,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) // wrong port connection - .connect('mydriver/0.0.0', basicWrongAuthToken()) + .connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicWrongAuthToken()) .then(() => done.fail('Should not initialize')) .catch(error => { expect(error).toBeDefined() @@ -200,7 +200,7 @@ describe('#integration ChannelConnection', () => { it('should have server version after connection initialization completed', async done => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) connection - .connect('mydriver/0.0.0', basicAuthToken()) + .connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(initializedConnection => { expect(initializedConnection).toBe(connection) const serverVersion = ServerVersion.fromString(connection.version) @@ -214,7 +214,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) connection - .connect('mydriver/0.0.0', basicWrongAuthToken()) + .connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicWrongAuthToken()) .then(() => done.fail('Should not connect')) .catch(initialError => { expect(initialError).toBeDefined() @@ -274,7 +274,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) connection - .connect('my-driver/1.2.3', basicAuthToken()) + .connect('my-driver/1.2.3', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(() => { connection .resetAndFlush() @@ -297,7 +297,7 @@ describe('#integration ChannelConnection', () => { it('should fail to reset and flush when FAILURE received', async done => { createConnection(`bolt://${sharedNeo4j.hostname}`) .then(connection => { - connection.connect('my-driver/1.2.3', basicAuthToken()).then(() => { + connection.connect('my-driver/1.2.3', 'mydriver/0.0.0 some system info', basicAuthToken()).then(() => { connection .resetAndFlush() .then(() => done.fail('Should fail')) @@ -325,7 +325,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) connection - .connect('my-driver/1.2.3', basicAuthToken()) + .connect('my-driver/1.2.3', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(() => { connection .resetAndFlush() @@ -353,7 +353,7 @@ describe('#integration ChannelConnection', () => { createConnection(`bolt://${sharedNeo4j.hostname}`) .then(connection => { connection - .connect('my-driver/1.2.3', basicAuthToken()) + .connect('my-driver/1.2.3', 'mydriver/0.0.0 some system info', basicAuthToken()) .then(() => { connection.protocol()._responseHandler._currentFailure = newError( 'Hello' @@ -419,7 +419,7 @@ describe('#integration ChannelConnection', () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) recordWrittenMessages(connection._protocol, messages) - await connection.connect('mydriver/0.0.0', basicAuthToken()) + await connection.connect('mydriver/0.0.0', 'mydriver/0.0.0 some system info', basicAuthToken()) expect(connection.isOpen()).toBeTruthy() await connection.close() @@ -436,7 +436,7 @@ describe('#integration ChannelConnection', () => { it('should not prepare broken connection to close', async () => { connection = await createConnection(`bolt://${sharedNeo4j.hostname}`) - await connection.connect('my-connection/9.9.9', basicAuthToken()) + await connection.connect('my-connection/9.9.9', 'mydriver/0.0.0 some system info', basicAuthToken()) expect(connection._protocol).toBeDefined() expect(connection._protocol).not.toBeNull() diff --git a/packages/neo4j-driver/test/internal/connection-delegate.test.js b/packages/neo4j-driver/test/internal/connection-delegate.test.js index 250f3085c..d88508442 100644 --- a/packages/neo4j-driver/test/internal/connection-delegate.test.js +++ b/packages/neo4j-driver/test/internal/connection-delegate.test.js @@ -128,7 +128,7 @@ describe('#unit DelegateConnection', () => { const spy = spyOn(delegate, 'connect') const connection = new DelegateConnection(delegate, null) - connection.connect('neo4j/js-driver', {}) + connection.connect('neo4j/js-driver', 'mydriver/0.0.0 some system info', {}) expect(spy).toHaveBeenCalledTimes(1) }) From 6c004be4edf8e183e77ca8c53ab2e72bcc916338 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 16 May 2023 18:34:05 +0200 Subject: [PATCH 11/18] Revert changes in the `userAgent` This property should not change the behaviour for backwards compatibility. The `userAgent` appears on the logs and it might be parsed by reporters. --- .../src/bolt/bolt-protocol-v1.js | 2 +- .../src/bolt/bolt-protocol-v3.js | 2 +- .../src/bolt/bolt-protocol-v4x1.js | 2 +- .../src/bolt/bolt-protocol-v4x3.js | 2 +- .../src/bolt/bolt-protocol-v5x0.js | 2 +- .../src/bolt/bolt-protocol-v5x1.js | 2 +- .../src/bolt/bolt-protocol-v5x2.js | 2 +- .../test/bolt/bolt-protocol-v1.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v2.test.js | 24 ++++++++++++ .../test/bolt/bolt-protocol-v3.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v4x0.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v4x1.test.js | 28 ++++++++++++++ .../test/bolt/bolt-protocol-v4x2.test.js | 28 ++++++++++++++ .../test/bolt/bolt-protocol-v4x3.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v4x4.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v5x0.test.js | 23 +++++++++++ .../test/bolt/bolt-protocol-v5x1.test.js | 38 +++++++++++++++++++ .../test/bolt/bolt-protocol-v5x2.test.js | 11 ++++-- .../test/bolt/bolt-protocol-v5x3.test.js | 38 +++++++++++++++++++ .../bolt-connection/bolt/bolt-protocol-v1.js | 2 +- .../bolt-connection/bolt/bolt-protocol-v3.js | 2 +- .../bolt/bolt-protocol-v4x1.js | 2 +- .../bolt/bolt-protocol-v4x3.js | 2 +- .../bolt/bolt-protocol-v5x0.js | 2 +- .../bolt/bolt-protocol-v5x1.js | 2 +- .../bolt/bolt-protocol-v5x2.js | 2 +- packages/neo4j-driver-deno/lib/mod.ts | 3 ++ packages/neo4j-driver-lite/src/index.ts | 3 ++ packages/neo4j-driver/src/index.js | 4 ++ 29 files changed, 326 insertions(+), 17 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index 9922bd468..a76754854 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -184,7 +184,7 @@ export default class BoltProtocol { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.init(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) + this.write(RequestMessage.init(userAgent, authToken), observer, true) return observer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js index 2b0b095fc..98e5bb88f 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -78,7 +78,7 @@ export default class BoltProtocol extends BoltProtocolV2 { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) + this.write(RequestMessage.hello(userAgent, authToken), observer, true) return observer } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js index 8c16a1637..6d1b0f702 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js @@ -83,7 +83,7 @@ export default class BoltProtocol extends BoltProtocolV4 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js index 6252a1838..8713ae17b 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js @@ -104,7 +104,7 @@ export default class BoltProtocol extends BoltProtocolV42 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting, ['utc']), + RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js index 9ec0c4cac..222ca88fb 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js @@ -63,7 +63,7 @@ export default class BoltProtocol extends BoltProtocolV44 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index 04e342342..bb1c551ae 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -71,7 +71,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello5x1(userAgent === '' || userAgent == null ? boltAgent : userAgent, this._serversideRouting), + RequestMessage.hello5x1(userAgent, this._serversideRouting), observer, false ) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js index 5e63110a6..6e22694fd 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x2.js @@ -69,7 +69,7 @@ export default class BoltProtocol extends BoltProtocolV5x1 { this.write( // if useragent is null then for all versions before 5.3 it should be bolt agent by default - RequestMessage.hello5x2(userAgent === '' || userAgent == null ? boltAgent : userAgent, notificationFilter, this._serversideRouting), + RequestMessage.hello5x2(userAgent, notificationFilter, this._serversideRouting), observer, false ) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js index cae56c8d6..c80c96b5d 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js @@ -104,6 +104,29 @@ describe('#unit BoltProtocolV1', () => { expect(protocol.flushes).toEqual([true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV1(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.init(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should run a query', () => { const recorder = new utils.MessageRecordingConnection() const protocol = utils.spyProtocolWrite( diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js index 651fc4c95..77d1a52b4 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js @@ -18,6 +18,7 @@ */ import BoltProtocolV2 from '../../src/bolt/bolt-protocol-v2' +import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' import { @@ -44,6 +45,29 @@ describe('#unit BoltProtocolV2', () => { expect.extend(utils.matchers) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV2(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.init(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should return correct bolt version number', () => { const protocol = new BoltProtocolV2(null, null, false) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js index 8902ffe9e..102810ee1 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js @@ -89,6 +89,29 @@ describe('#unit BoltProtocolV3', () => { expect(protocol.flushes).toEqual([true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should run a query', () => { const bookmarks = new Bookmarks([ 'neo4j:bookmark:v1:tx1', diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js index e37f96c74..0266dde62 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js @@ -57,6 +57,29 @@ describe('#unit BoltProtocolV4x0', () => { expect.extend(utils.matchers) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x0(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should run a query', () => { const database = 'testdb' const bookmarks = new Bookmarks([ diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js index c921f6a17..1da1c4093 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js @@ -18,6 +18,7 @@ */ import BoltProtocolV4x1 from '../../src/bolt/bolt-protocol-v4x1' +import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' import { Date, @@ -45,6 +46,33 @@ const { } = internal describe('#unit BoltProtocolV4x1', () => { + beforeEach(() => { + expect.extend(utils.matchers) + }) + + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x1(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + describe('Bolt v4.4', () => { /** * @param {string} impersonatedUser The impersonated user. diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js index 6e16b1dee..5caa1bc92 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js @@ -18,6 +18,7 @@ */ import BoltProtocolV4x2 from '../../src/bolt/bolt-protocol-v4x2' +import RequestMessage from '../../src/bolt/request-message' import utils from '../test-utils' import { Date, @@ -45,6 +46,33 @@ const { } = internal describe('#unit BoltProtocolV4x2', () => { + beforeEach(() => { + expect.extend(utils.matchers) + }) + + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x2(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + describe('Bolt v4.4', () => { /** * @param {string} impersonatedUser The impersonated user. diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js index 698c8dd19..53e0f9626 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -206,6 +206,29 @@ describe('#unit BoltProtocolV4x3', () => { expect(protocol.flushes).toEqual([true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken, null, ['utc']) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should begin a transaction', () => { const bookmarks = new Bookmarks([ 'neo4j:bookmark:v1:tx1', diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js index 8c4ed5209..fdd6bb231 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -280,6 +280,29 @@ describe('#unit BoltProtocolV4x4', () => { expect(protocol.flushes).toEqual([true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x4(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken, null, ['utc']) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should begin a transaction', () => { const bookmarks = new Bookmarks([ 'neo4j:bookmark:v1:tx1', diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js index 3fcdeb935..3a4a1a90f 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -280,6 +280,29 @@ describe('#unit BoltProtocolV5x0', () => { expect(protocol.flushes).toEqual([true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x0(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(userAgent, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + it('should begin a transaction', () => { const bookmarks = new Bookmarks([ 'neo4j:bookmark:v1:tx1', diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js index 8c472ba42..7da616d38 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -295,6 +295,44 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([false, true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x1(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(2) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello5x1(userAgent) + ) + expect(protocol.messages[1]).toBeMessage( + RequestMessage.logon(authToken) + ) + + expect(protocol.observers.length).toBe(2) + + // hello observer + const helloObserver = protocol.observers[0] + expect(helloObserver).toBeInstanceOf(LoginObserver) + expect(helloObserver).not.toBe(observer) + + // login observer + const loginObserver = protocol.observers[1] + expect(loginObserver).toBeInstanceOf(LoginObserver) + expect(loginObserver).toBe(observer) + + expect(protocol.flushes).toEqual([false, true]) + }) + it.each( [true, false] )('should logon to the server [flush=%s]', (flush) => { diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js index b55cd31c0..5c90f4d49 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x2.test.js @@ -295,7 +295,12 @@ describe('#unit BoltProtocolV5x2', () => { expect(protocol.flushes).toEqual([false, true]) }) - it('should set userAgent to bolt agent when userAgent is null', () => { + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { const recorder = new utils.MessageRecordingConnection() const protocol = new BoltProtocolV5x2(recorder, null, false) utils.spyProtocolWrite(protocol) @@ -303,11 +308,11 @@ describe('#unit BoltProtocolV5x2', () => { const clientName = 'js-driver/1.2.3' const authToken = { username: 'neo4j', password: 'secret' } - const observer = protocol.initialize({ userAgent: null, boltAgent: clientName, authToken }) + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) protocol.verifyMessageCount(2) expect(protocol.messages[0]).toBeMessage( - RequestMessage.hello5x1(clientName) + RequestMessage.hello5x1(userAgent) ) expect(protocol.messages[1]).toBeMessage( RequestMessage.logon(authToken) diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js index 5d7dc56f5..4b70b5891 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js @@ -296,6 +296,44 @@ describe('#unit BoltProtocolV5x3', () => { expect(protocol.flushes).toEqual([false, true]) }) + it.each([ + 'javascript-driver/5.5.0', + '', + undefined, + null + ])('should always use the user agent set by the user', (userAgent) => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x3(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } + + const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + + protocol.verifyMessageCount(2) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello5x3(userAgent, clientName) + ) + expect(protocol.messages[1]).toBeMessage( + RequestMessage.logon(authToken) + ) + + expect(protocol.observers.length).toBe(2) + + // hello observer + const helloObserver = protocol.observers[0] + expect(helloObserver).toBeInstanceOf(LoginObserver) + expect(helloObserver).not.toBe(observer) + + // login observer + const loginObserver = protocol.observers[1] + expect(loginObserver).toBeInstanceOf(LoginObserver) + expect(loginObserver).toBe(observer) + + expect(protocol.flushes).toEqual([false, true]) + }) + it.each( [true, false] )('should logon to the server [flush=%s]', (flush) => { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js index 5aceb8a47..b524adfaa 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v1.js @@ -184,7 +184,7 @@ export default class BoltProtocol { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.init(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) + this.write(RequestMessage.init(userAgent, authToken), observer, true) return observer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js index 381f703b3..2025d4c21 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v3.js @@ -78,7 +78,7 @@ export default class BoltProtocol extends BoltProtocolV2 { // passing notification filter on this protocol version throws an error assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) - this.write(RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken), observer, true) + this.write(RequestMessage.hello(userAgent, authToken), observer, true) return observer } diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js index 2fd503c41..28e4d25db 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x1.js @@ -83,7 +83,7 @@ export default class BoltProtocol extends BoltProtocolV4 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js index 2a913a1da..15aded3c1 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x3.js @@ -104,7 +104,7 @@ export default class BoltProtocol extends BoltProtocolV42 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting, ['utc']), + RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js index bc489f7bf..5a252a5fc 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x0.js @@ -63,7 +63,7 @@ export default class BoltProtocol extends BoltProtocolV44 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello(userAgent === '' || userAgent == null ? boltAgent : userAgent, authToken, this._serversideRouting), + RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, true ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js index 610b9a54b..642dad6b8 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js @@ -71,7 +71,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { assertNotificationFilterIsEmpty(notificationFilter, this._onProtocolError, observer) this.write( - RequestMessage.hello5x1(userAgent === '' || userAgent == null ? boltAgent : userAgent, this._serversideRouting), + RequestMessage.hello5x1(userAgent, this._serversideRouting), observer, false ) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js index ab1e2e869..4e2d929c4 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x2.js @@ -69,7 +69,7 @@ export default class BoltProtocol extends BoltProtocolV5x1 { this.write( // if useragent is null then for all versions before 5.3 it should be bolt agent by default - RequestMessage.hello5x2(userAgent === '' || userAgent == null ? boltAgent : userAgent, notificationFilter, this._serversideRouting), + RequestMessage.hello5x2(userAgent, notificationFilter, this._serversideRouting), observer, false ) diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 9421e9dca..339fbf4f6 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -118,6 +118,8 @@ const { urlUtil } = internal +const USER_AGENT = 'neo4j-javascript/' + VERSION + function isAuthTokenManager (value: unknown): value is AuthTokenManager { if (typeof value === 'object' && value != null && @@ -333,6 +335,7 @@ function driver ( const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. + config.userAgent = config.userAgent ?? USER_AGENT config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 8542e9062..857c6e404 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -117,6 +117,8 @@ const { urlUtil } = internal +const USER_AGENT = 'neo4j-javascript/' + VERSION + function isAuthTokenManager (value: unknown): value is AuthTokenManager { if (typeof value === 'object' && value != null && @@ -332,6 +334,7 @@ function driver ( const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. + config.userAgent = config.userAgent ?? USER_AGENT config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 6ba8d557e..2cc318006 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -93,6 +93,8 @@ const { urlUtil } = internal +const USER_AGENT = 'neo4j-javascript/' + VERSION + function isAuthTokenManager (value) { return typeof value === 'object' && value != null && @@ -298,6 +300,8 @@ function driver (url, authToken, config = {}) { const authTokenManager = createAuthManager(authToken) + // Use default user agent or user agent specified by user. + config.userAgent = config.userAgent || USER_AGENT config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) From 5b020223e14634b90c1a66066608acfc79f84f96 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 16 May 2023 19:15:47 +0200 Subject: [PATCH 12/18] Improve Deno instructions --- packages/neo4j-driver-deno/README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/neo4j-driver-deno/README.md b/packages/neo4j-driver-deno/README.md index 26eaa3cec..e09e890c5 100644 --- a/packages/neo4j-driver-deno/README.md +++ b/packages/neo4j-driver-deno/README.md @@ -48,8 +48,11 @@ await session.close(); await driver.close(); ``` -You can use `deno run --allow-net ...` or `deno repl` to run this example. If -you don't have a running Neo4j instance, you can use +You can use `deno run --allow-net --allow-sys...` or `deno repl` to run this example. + +For Deno versions bellow `1.27.1`, you should use the flag `--allow-env` instead of `--allow-sys`. + +If you don't have a running Neo4j instance, you can use `docker run --rm -p 7687:7687 -e NEO4J_AUTH=neo4j/driverdemo neo4j:4.4` to quickly spin one up. From 4a8e3e4c1e7cfd1f5f2173a0fe9cf35d1a7be90c Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 17 May 2023 15:49:27 +0200 Subject: [PATCH 13/18] Implements BoltAgent as structure The BoltAgent was refactoried as structure to improve the log parser. --- .../src/bolt/request-message.js | 11 +- .../test/bolt/bolt-protocol-v5x3.test.js | 16 ++- .../test/bolt/request-message.test.js | 135 +++++++++++++++--- .../internal/bolt-agent/browser/bolt-agent.ts | 33 ++++- .../internal/bolt-agent/deno/bolt-agent.ts | 52 +++++-- .../internal/bolt-agent/node/bolt-agent.ts | 50 ++++++- packages/core/src/types.ts | 9 +- .../bolt-agent/deno/bolt-agent.test.ts | 37 +++-- .../bolt-agent/browser/bolt-agent.test.ts | 14 +- .../bolt-agent/node/bolt-agent.test.ts | 39 +++-- packages/neo4j-driver-deno/generate.ts | 11 +- .../bolt-connection/bolt/request-message.js | 11 +- .../internal/bolt-agent/browser/bolt-agent.ts | 33 ++++- .../internal/bolt-agent/deno/bolt-agent.ts | 52 +++++-- .../internal/bolt-agent/node/bolt-agent.ts | 50 ++++++- packages/neo4j-driver-deno/lib/core/types.ts | 9 +- 16 files changed, 460 insertions(+), 102 deletions(-) diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index 4da2e2a5e..6ee13651d 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -200,12 +200,21 @@ export default class RequestMessage { * @return {RequestMessage} new HELLO message. */ static hello5x3 (userAgent, boltAgent, notificationFilter = null, routing = null) { - const metadata = { bolt_agent: boltAgent } + const metadata = { } if (userAgent) { metadata.user_agent = userAgent } + if (boltAgent) { + metadata.bolt_agent = { + product: boltAgent.product, + platform: boltAgent.platform, + language: boltAgent.language, + language_details: boltAgent.languageDetails + } + } + if (notificationFilter) { if (notificationFilter.minimumSeverityLevel) { metadata.notifications_minimum_severity = notificationFilter.minimumSeverityLevel diff --git a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js index 4b70b5891..c4cd84ce6 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x3.test.js @@ -268,7 +268,11 @@ describe('#unit BoltProtocolV5x3', () => { utils.spyProtocolWrite(protocol) const clientName = 'js-driver/1.2.3' - const boltAgent = 'js-driver/1.2.3 (bolt agent)' + const boltAgent = { + product: 'neo4j-javascript/5.6', + platform: 'netbsd 1.1.1; Some arch', + languageDetails: 'Node/16.0.1 (v8 1.7.0)' + } const authToken = { username: 'neo4j', password: 'secret' } const observer = protocol.initialize({ userAgent: clientName, boltAgent: boltAgent, authToken }) @@ -306,14 +310,18 @@ describe('#unit BoltProtocolV5x3', () => { const protocol = new BoltProtocolV5x3(recorder, null, false) utils.spyProtocolWrite(protocol) - const clientName = 'js-driver/1.2.3' + const boltAgent = { + product: 'neo4j-javascript/5.6', + platform: 'netbsd 1.1.1; Some arch', + languageDetails: 'Node/16.0.1 (v8 1.7.0)' + } const authToken = { username: 'neo4j', password: 'secret' } - const observer = protocol.initialize({ userAgent, boltAgent: clientName, authToken }) + const observer = protocol.initialize({ userAgent, boltAgent, authToken }) protocol.verifyMessageCount(2) expect(protocol.messages[0]).toBeMessage( - RequestMessage.hello5x3(userAgent, clientName) + RequestMessage.hello5x3(userAgent, boltAgent) ) expect(protocol.messages[1]).toBeMessage( RequestMessage.logon(authToken) diff --git a/packages/bolt-connection/test/bolt/request-message.test.js b/packages/bolt-connection/test/bolt/request-message.test.js index 7f86f9d09..53d14c511 100644 --- a/packages/bolt-connection/test/bolt/request-message.test.js +++ b/packages/bolt-connection/test/bolt/request-message.test.js @@ -531,22 +531,125 @@ describe('#unit RequestMessage', () => { ) }) }) + }) - function notificationFilterFixtures () { - return notificationFilterBehaviour.notificationFilterFixture() - .map(notificationFilter => { - const expectedNotificationFilter = {} - if (notificationFilter) { - if (notificationFilter.minimumSeverityLevel) { - expectedNotificationFilter.notifications_minimum_severity = notificationFilter.minimumSeverityLevel - } - - if (notificationFilter.disabledCategories) { - expectedNotificationFilter.notifications_disabled_categories = notificationFilter.disabledCategories - } - } - return [notificationFilter, expectedNotificationFilter] - }) - } + describe('Bolt5.3', () => { + it('should create HELLO with NodeJS Bolt Agent', () => { + const userAgent = 'my-driver/1.0.2' + const boltAgent = { + product: 'neo4j-javascript/5.6', + platform: 'netbsd 1.1.1; Some arch', + languageDetails: 'Node/16.0.1 (v8 1.7.0)' + } + + const expectedFields = { + user_agent: userAgent, + bolt_agent: { + product: 'neo4j-javascript/5.6', + platform: 'netbsd 1.1.1; Some arch', + language_details: 'Node/16.0.1 (v8 1.7.0)' + } + } + + const message = RequestMessage.hello5x3(userAgent, boltAgent) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + expectedFields + ]) + expect(message.toString()).toEqual( + `HELLO ${json.stringify(expectedFields)}` + ) + }) + + it('should create HELLO with Browser Bolt Agent', () => { + const userAgent = 'my-driver/1.0.2' + + const boltAgent = { + product: 'neo4j-javascript/5.3', + platform: 'Macintosh; Intel Mac OS X 10_15_7' + } + + const expectedFields = { + user_agent: userAgent, + bolt_agent: { + product: 'neo4j-javascript/5.3', + platform: 'Macintosh; Intel Mac OS X 10_15_7' + } + } + + const message = RequestMessage.hello5x3(userAgent, boltAgent) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + expectedFields + ]) + expect(message.toString()).toEqual( + `HELLO ${json.stringify(expectedFields)}` + ) + }) + + it('should create HELLO with Deno Bolt Agent', () => { + const userAgent = 'my-driver/1.0.2' + + const boltAgent = { + product: 'neo4j-javascript/5.3', + platform: 'macos 14.1; myArch', + languageDetails: 'Deno/1.19.1 (v8 8.1.39)' + } + + const expectedFields = { + user_agent: userAgent, + bolt_agent: { + product: 'neo4j-javascript/5.3', + platform: 'macos 14.1; myArch', + language_details: 'Deno/1.19.1 (v8 8.1.39)' + } + } + + const message = RequestMessage.hello5x3(userAgent, boltAgent) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + expectedFields + ]) + expect(message.toString()).toEqual( + `HELLO ${json.stringify(expectedFields)}` + ) + }) + + it.each( + notificationFilterFixtures() + )('should create HELLO message where notificationFilters=%o', (notificationFilter, expectedNotificationFilter) => { + const userAgent = 'my-driver/1.0.2' + const message = RequestMessage.hello5x3(userAgent, undefined, notificationFilter) + + const expectedFields = { user_agent: userAgent, ...expectedNotificationFilter } + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + expectedFields + ]) + expect(message.toString()).toEqual( + `HELLO ${json.stringify(expectedFields)}` + ) + }) }) + + function notificationFilterFixtures () { + return notificationFilterBehaviour.notificationFilterFixture() + .map(notificationFilter => { + const expectedNotificationFilter = {} + if (notificationFilter) { + if (notificationFilter.minimumSeverityLevel) { + expectedNotificationFilter.notifications_minimum_severity = notificationFilter.minimumSeverityLevel + } + + if (notificationFilter.disabledCategories) { + expectedNotificationFilter.notifications_disabled_categories = notificationFilter.disabledCategories + } + } + return [notificationFilter, expectedNotificationFilter] + }) + } }) diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index 4e2c97390..69755f85f 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -17,14 +17,35 @@ * limitations under the License. */ /* eslint-disable */ -// @ts-ignore: browser code so must be skipped by ts -export function fromVersion (version: string, windowProvider = () => window): string { - // @ts-ignore: browser code so must be skipped by ts - const APP_VERSION = windowProvider().navigator.appVersion + +import { BoltAgent } from "../../../types"; + +interface SystemInfo { + appVersion: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + // @ts-ignore: browser code so must be skipped by ts + get appVersion(): window.navigator.appVersion + }) +): BoltAgent { + const systemInfo = getSystemInfo() //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = APP_VERSION.split("(")[1].split(")")[0]; + const OS = systemInfo.appVersion.split("(")[1].split(")")[0]; - return `neo4j-javascript/${version} (${OS})` + return { + product: `neo4j-javascript/${version}`, + platform: OS + } } /* eslint-enable */ diff --git a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts index d2c42b785..001d4776b 100644 --- a/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/deno/bolt-agent.ts @@ -17,17 +17,49 @@ * limitations under the License. */ +import { BoltAgent } from '../../../types.ts' + +export interface SystemInfo { + hostArch: string + denoVersion: string + v8Version: string + osVersion: string + osRelease: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ /* eslint-disable */ -export function fromVersion (version: string): string { - //@ts-ignore - const HOST_ARCH = Deno.build.arch - //@ts-ignore - const DENO_VERSION = `Deno/${Deno.version.deno}` - //@ts-ignore - const NODE_V8_VERSION = Deno.version.v8 - //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + //@ts-ignore + hostArch: Deno.build.arch, + //@ts-ignore + denoVersion: Deno.version.deno, + //@ts-ignore: + v8Version: Deno.version.v8, + //@ts-ignore + osVersion: Deno.build.os, + get osRelease() { + //@ts-ignore + return Deno.osRelease ? Deno.osRelease() : '' + } + }) +): BoltAgent { + const systemInfo = getSystemInfo() + const DENO_VERSION = `Deno/${systemInfo.denoVersion}` + const OS_NAME_VERSION = `${systemInfo.osVersion} ${systemInfo.osRelease}`.trim() - return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` + return { + product: `neo4j-javascript/${version}`, + platform: `${OS_NAME_VERSION}; ${systemInfo.hostArch}`, + languageDetails: `${DENO_VERSION} (v8 ${systemInfo.v8Version})` + } } /* eslint-enable */ diff --git a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts index fe76a0a95..e59921d12 100644 --- a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts @@ -16,13 +16,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as os from 'os' +import { platform, release } from 'os' +import { BoltAgent } from '../../../types' -export function fromVersion (version: string): string { - const HOST_ARCH = process.config.variables.host_arch - const NODE_VERSION = 'Node/' + process.versions.node - const NODE_V8_VERSION = process.versions.v8 - const OS_NAME_VERSION = `${os.platform()} ${os.release()}` +interface SystemInfo { + hostArch: string + nodeVersion: string + v8Version: string + platform: NodeJS.Platform + release: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + hostArch: process.config.variables.host_arch, + nodeVersion: process.versions.node, + v8Version: process.versions.v8, + get platform () { + return platform() + }, + get release () { + return release() + } + }) +): BoltAgent { + const systemInfo = getSystemInfo() + const HOST_ARCH = systemInfo.hostArch + const NODE_VERSION = 'Node/' + systemInfo.nodeVersion + const NODE_V8_VERSION = systemInfo.v8Version + const OS_NAME_VERSION = `${systemInfo.platform} ${systemInfo.release}` - return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` + return { + product: `neo4j-javascript/${version}`, + platform: `${OS_NAME_VERSION}; ${HOST_ARCH}`, + languageDetails: `${NODE_VERSION} (v8 ${NODE_V8_VERSION})` + } } + +export type { SystemInfo } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index c509145d3..b1c447e1e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -49,6 +49,13 @@ export interface AuthToken { parameters?: Parameters } +export interface BoltAgent { + product?: string + platform?: string + language?: string + languageDetails?: string +} + export interface Config { encrypted?: boolean | EncryptionLevel trust?: TrustStrategy @@ -65,7 +72,7 @@ export interface Config { logging?: LoggingConfig resolver?: (address: string) => string[] | Promise userAgent?: string - boltAgent?: string + boltAgent?: BoltAgent } /** diff --git a/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts index 20929339a..9e457984f 100644 --- a/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts +++ b/packages/core/test/deno/internal/bolt-agent/deno/bolt-agent.test.ts @@ -18,16 +18,9 @@ */ /* eslint-disable */ //@ts-ignore -import { fromVersion } from '../../../../../src/internal/bolt-agent/deno/bolt-agent.ts' +import { fromVersion, SystemInfo } from '../../../../../src/internal/bolt-agent/deno/bolt-agent.ts' //@ts-ignore -import { assertMatch, assertEquals } from "https://deno.land/std@0.182.0/testing/asserts.ts"; - -//@ts-ignore -Deno.test('Test Contains deno and javascript', () => { - const boltAgent = fromVersion("5.3") - assertMatch(boltAgent, new RegExp("^neo4j-javascript/5.3")) - assertMatch(boltAgent, new RegExp("Deno/")) -}); +import { assertEquals } from "https://deno.land/std@0.182.0/testing/asserts.ts"; //@ts-ignore Deno.test('Test full bolt agent', () => { @@ -40,8 +33,30 @@ Deno.test('Test full bolt agent', () => { //@ts-ignore const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() - const boltAgentExpected = `neo4j-javascript/5.3 (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` + const boltAgent = fromVersion('5.3') + + assertEquals(boltAgent, { + product: 'neo4j-javascript/5.3', + platform: `${OS_NAME_VERSION}; ${HOST_ARCH}`, + languageDetails: `${DENO_VERSION} (v8 ${NODE_V8_VERSION})` + }) +}); + +// @ts-ignore +Deno.test('Test full bolt agent for mocked values', () => { + const systemInfo: SystemInfo = { + hostArch: 'myArch', + denoVersion: '1.19.1', + v8Version: '8.1.39', + osVersion: 'macos', + osRelease: '14.1' + } + const boltAgent = fromVersion('5.3', () => systemInfo) - assertEquals(fromVersion("5.3"), boltAgentExpected) + assertEquals(boltAgent, { + product: 'neo4j-javascript/5.3', + platform: 'macos 14.1; myArch', + languageDetails: `Deno/1.19.1 (v8 8.1.39)` + }) }); /* eslint-enable */ diff --git a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts index 210ffac61..e78020574 100644 --- a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts +++ b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts @@ -22,17 +22,17 @@ describe('#unit boltAgent', () => { // This test is very fragile but the exact look of this string should not change without PM approval it('should return the correct bolt agent for specified version', () => { const version = '5.3' - const windowProvider = (): any => { + const getSystemInfo = (): any => { return { - navigator: { - appVersion: '5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' - } + appVersion: '5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' } } - const boltAgent = fromVersion(version, windowProvider) + const boltAgent = fromVersion(version, getSystemInfo) - expect(boltAgent.length === 0).toBeFalsy() - expect(boltAgent).toEqual(`neo4j-javascript/${version} (Macintosh; Intel Mac OS X 10_15_7)`) + expect(boltAgent).toEqual({ + product: 'neo4j-javascript/5.3', + platform: 'Macintosh; Intel Mac OS X 10_15_7' + }) }) }) diff --git a/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts index 5804202ad..2a85c8200 100644 --- a/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts +++ b/packages/core/test/internal/bolt-agent/node/bolt-agent.test.ts @@ -17,27 +17,42 @@ * limitations under the License. */ import { boltAgent as BoltAgent } from '../../../../src/internal' -import os from 'os' +import { SystemInfo } from '../../../../src/internal/bolt-agent' +import { platform, release } from 'os' describe('#unit boltAgent', () => { - // This test is very fragile but the exact look of this string should not change without PM approval + // This test is very fragile but the exact look of this struct should not change without PM approval it('should return the correct bolt agent for specified version', () => { const version = '5.3' - const boltAgent = BoltAgent.fromVersion(version) - const HOST_ARCH = process.config.variables.host_arch const NODE_VERSION = 'Node/' + process.versions.node const NODE_V8_VERSION = process.versions.v8 - const osName = os.platform() + const boltAgent = BoltAgent.fromVersion(version) + + expect(boltAgent).toEqual({ + product: `neo4j-javascript/${version}`, + platform: `${platform()} ${release()}; ${HOST_ARCH}`, + languageDetails: `${NODE_VERSION} (v8 ${NODE_V8_VERSION})` + }) + }) + + it('should return the correct bolt agent for mocked values', () => { + const version = '5.6' + const systemInfo: SystemInfo = { + hostArch: 'Some arch', + nodeVersion: '16.0.1', + v8Version: '1.7.0', + platform: 'netbsd', + release: '1.1.1' + } - expect(boltAgent.length === 0).toBeFalsy() - expect(boltAgent).toContain(`neo4j-javascript/${version}`) - expect(boltAgent).toContain(`${HOST_ARCH}`) - expect(boltAgent).toContain(`${NODE_VERSION}`) - expect(boltAgent).toContain(`${NODE_V8_VERSION}`) - expect(boltAgent).toContain(`${osName}`) + const boltAgent = BoltAgent.fromVersion(version, () => systemInfo) - expect(boltAgent).toEqual(`neo4j-javascript/${version} (${osName} ${os.release()}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})`) + expect(boltAgent).toEqual({ + product: 'neo4j-javascript/5.6', + platform: 'netbsd 1.1.1; Some arch', + languageDetails: 'Node/16.0.1 (v8 1.7.0)' + }) }) }) diff --git a/packages/neo4j-driver-deno/generate.ts b/packages/neo4j-driver-deno/generate.ts index 02d3c0565..ea523c2e4 100644 --- a/packages/neo4j-driver-deno/generate.ts +++ b/packages/neo4j-driver-deno/generate.ts @@ -24,6 +24,13 @@ const isDir = (path: string) => { } }; +const addExtIfNeeded = (path: string, ext: string): string => { + if (path.endsWith(ext)) { + return path; + } + return `${path}${ext}` +} + //////////////////////////////////////////////////////////////////////////////// // Parse arguments const parsedArgs = parse(Deno.args, { @@ -86,7 +93,7 @@ async function copyAndTransform(inDir: string, outDir: string) { (_x, origPath) => { const newPath = isDir(`${inDir}/${origPath}`) ? `${origPath}/index.ts` - : `${origPath}.ts`; + : addExtIfNeeded(origPath, '.ts'); return ` from '${newPath}'`; }, ); @@ -109,7 +116,7 @@ async function copyAndTransform(inDir: string, outDir: string) { (_x, origPath) => { const newPath = isDir(`${inDir}/${origPath}`) ? `${origPath}/index.js` - : `${origPath}.js`; + : addExtIfNeeded(origPath, '.js'); return ` from '${newPath}'`; }, ); diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js index 6822f9c4f..f86c08ab7 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/request-message.js @@ -200,12 +200,21 @@ export default class RequestMessage { * @return {RequestMessage} new HELLO message. */ static hello5x3 (userAgent, boltAgent, notificationFilter = null, routing = null) { - const metadata = { bolt_agent: boltAgent } + const metadata = { } if (userAgent) { metadata.user_agent = userAgent } + if (boltAgent) { + metadata.bolt_agent = { + product: boltAgent.product, + platform: boltAgent.platform, + language: boltAgent.language, + language_details: boltAgent.languageDetails + } + } + if (notificationFilter) { if (notificationFilter.minimumSeverityLevel) { metadata.notifications_minimum_severity = notificationFilter.minimumSeverityLevel diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts index 4e2c97390..69755f85f 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -17,14 +17,35 @@ * limitations under the License. */ /* eslint-disable */ -// @ts-ignore: browser code so must be skipped by ts -export function fromVersion (version: string, windowProvider = () => window): string { - // @ts-ignore: browser code so must be skipped by ts - const APP_VERSION = windowProvider().navigator.appVersion + +import { BoltAgent } from "../../../types"; + +interface SystemInfo { + appVersion: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + // @ts-ignore: browser code so must be skipped by ts + get appVersion(): window.navigator.appVersion + }) +): BoltAgent { + const systemInfo = getSystemInfo() //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = APP_VERSION.split("(")[1].split(")")[0]; + const OS = systemInfo.appVersion.split("(")[1].split(")")[0]; - return `neo4j-javascript/${version} (${OS})` + return { + product: `neo4j-javascript/${version}`, + platform: OS + } } /* eslint-enable */ diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts index d2c42b785..001d4776b 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/deno/bolt-agent.ts @@ -17,17 +17,49 @@ * limitations under the License. */ +import { BoltAgent } from '../../../types.ts' + +export interface SystemInfo { + hostArch: string + denoVersion: string + v8Version: string + osVersion: string + osRelease: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ /* eslint-disable */ -export function fromVersion (version: string): string { - //@ts-ignore - const HOST_ARCH = Deno.build.arch - //@ts-ignore - const DENO_VERSION = `Deno/${Deno.version.deno}` - //@ts-ignore - const NODE_V8_VERSION = Deno.version.v8 - //@ts-ignore - const OS_NAME_VERSION = `${Deno.build.os} ${Deno.osRelease ? Deno.osRelease() : ''}`.trim() +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + //@ts-ignore + hostArch: Deno.build.arch, + //@ts-ignore + denoVersion: Deno.version.deno, + //@ts-ignore: + v8Version: Deno.version.v8, + //@ts-ignore + osVersion: Deno.build.os, + get osRelease() { + //@ts-ignore + return Deno.osRelease ? Deno.osRelease() : '' + } + }) +): BoltAgent { + const systemInfo = getSystemInfo() + const DENO_VERSION = `Deno/${systemInfo.denoVersion}` + const OS_NAME_VERSION = `${systemInfo.osVersion} ${systemInfo.osRelease}`.trim() - return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${DENO_VERSION} (v8 ${NODE_V8_VERSION})` + return { + product: `neo4j-javascript/${version}`, + platform: `${OS_NAME_VERSION}; ${systemInfo.hostArch}`, + languageDetails: `${DENO_VERSION} (v8 ${systemInfo.v8Version})` + } } /* eslint-enable */ diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts index fe76a0a95..28b605fdf 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts @@ -16,13 +16,49 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import * as os from 'os' +import { platform, release } from 'os' +import { BoltAgent } from '../../../types.ts' -export function fromVersion (version: string): string { - const HOST_ARCH = process.config.variables.host_arch - const NODE_VERSION = 'Node/' + process.versions.node - const NODE_V8_VERSION = process.versions.v8 - const OS_NAME_VERSION = `${os.platform()} ${os.release()}` +interface SystemInfo { + hostArch: string + nodeVersion: string + v8Version: string + platform: NodeJS.Platform + release: string +} + +/** + * Constructs a BoltAgent structure from a given product version. + * + * @param {string} version The product version + * @param {function():SystemInfo} getSystemInfo Parameter used of inject system information and mock calls to the APIs. + * @returns {BoltAgent} The bolt agent + */ +export function fromVersion ( + version: string, + getSystemInfo: () => SystemInfo = () => ({ + hostArch: process.config.variables.host_arch, + nodeVersion: process.versions.node, + v8Version: process.versions.v8, + get platform () { + return platform() + }, + get release () { + return release() + } + }) +): BoltAgent { + const systemInfo = getSystemInfo() + const HOST_ARCH = systemInfo.hostArch + const NODE_VERSION = 'Node/' + systemInfo.nodeVersion + const NODE_V8_VERSION = systemInfo.v8Version + const OS_NAME_VERSION = `${systemInfo.platform} ${systemInfo.release}` - return `neo4j-javascript/${version} (${OS_NAME_VERSION}; ${HOST_ARCH}) ${NODE_VERSION} (v8 ${NODE_V8_VERSION})` + return { + product: `neo4j-javascript/${version}`, + platform: `${OS_NAME_VERSION}; ${HOST_ARCH}`, + languageDetails: `${NODE_VERSION} (v8 ${NODE_V8_VERSION})` + } } + +export type { SystemInfo } diff --git a/packages/neo4j-driver-deno/lib/core/types.ts b/packages/neo4j-driver-deno/lib/core/types.ts index c509145d3..b1c447e1e 100644 --- a/packages/neo4j-driver-deno/lib/core/types.ts +++ b/packages/neo4j-driver-deno/lib/core/types.ts @@ -49,6 +49,13 @@ export interface AuthToken { parameters?: Parameters } +export interface BoltAgent { + product?: string + platform?: string + language?: string + languageDetails?: string +} + export interface Config { encrypted?: boolean | EncryptionLevel trust?: TrustStrategy @@ -65,7 +72,7 @@ export interface Config { logging?: LoggingConfig resolver?: (address: string) => string[] | Promise userAgent?: string - boltAgent?: string + boltAgent?: BoltAgent } /** From 26d236642cade914d3d2b1c16f94b6f8e06523cc Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 24 May 2023 13:47:34 +0200 Subject: [PATCH 14/18] Handling null/undefined platform information in Browser --- .../internal/bolt-agent/browser/bolt-agent.ts | 10 ++++--- .../bolt-agent/browser/bolt-agent.test.ts | 29 +++++++++++++++++++ .../internal/bolt-agent/browser/bolt-agent.ts | 10 ++++--- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index 69755f85f..b2d2a554c 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -21,7 +21,7 @@ import { BoltAgent } from "../../../types"; interface SystemInfo { - appVersion: string + appVersion?: string } /** @@ -34,14 +34,16 @@ interface SystemInfo { export function fromVersion ( version: string, getSystemInfo: () => SystemInfo = () => ({ - // @ts-ignore: browser code so must be skipped by ts - get appVersion(): window.navigator.appVersion + get appVersion() { + // @ts-ignore: browser code so must be skipped by ts + return window.navigator.appVersion + } }) ): BoltAgent { const systemInfo = getSystemInfo() //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = systemInfo.appVersion.split("(")[1].split(")")[0]; + const OS = systemInfo.appVersion != null ? systemInfo.appVersion.split("(")[1].split(")")[0] : undefined return { product: `neo4j-javascript/${version}`, diff --git a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts index e78020574..3d57e2c90 100644 --- a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts +++ b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts @@ -35,4 +35,33 @@ describe('#unit boltAgent', () => { platform: 'Macintosh; Intel Mac OS X 10_15_7' }) }) + + it('should handle null appVersion', () => { + const version = '5.3' + const getSystemInfo = (): any => { + return { + appVersion: null + } + } + + const boltAgent = fromVersion(version, getSystemInfo) + + expect(boltAgent).toEqual({ + product: 'neo4j-javascript/5.3' + }) + }) + + it('should handle undefined appVersion', () => { + const version = '5.3' + const getSystemInfo = (): any => { + return { + } + } + + const boltAgent = fromVersion(version, getSystemInfo) + + expect(boltAgent).toEqual({ + product: 'neo4j-javascript/5.3' + }) + }) }) diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts index 69755f85f..b2d2a554c 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -21,7 +21,7 @@ import { BoltAgent } from "../../../types"; interface SystemInfo { - appVersion: string + appVersion?: string } /** @@ -34,14 +34,16 @@ interface SystemInfo { export function fromVersion ( version: string, getSystemInfo: () => SystemInfo = () => ({ - // @ts-ignore: browser code so must be skipped by ts - get appVersion(): window.navigator.appVersion + get appVersion() { + // @ts-ignore: browser code so must be skipped by ts + return window.navigator.appVersion + } }) ): BoltAgent { const systemInfo = getSystemInfo() //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = systemInfo.appVersion.split("(")[1].split(")")[0]; + const OS = systemInfo.appVersion != null ? systemInfo.appVersion.split("(")[1].split(")")[0] : undefined return { product: `neo4j-javascript/${version}`, From 5d2a3eb7c586a5ec7960cfaba4dd192010392493 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 24 May 2023 14:13:03 +0200 Subject: [PATCH 15/18] Use user agent instead of appVersion --- .../src/internal/bolt-agent/browser/bolt-agent.ts | 15 +++++++++------ .../bolt-agent/browser/bolt-agent.test.ts | 12 +++++++----- .../internal/bolt-agent/browser/bolt-agent.ts | 15 +++++++++------ 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts index b2d2a554c..ae29b1048 100644 --- a/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/browser/bolt-agent.ts @@ -21,7 +21,7 @@ import { BoltAgent } from "../../../types"; interface SystemInfo { - appVersion?: string + userAgent?: string } /** @@ -34,20 +34,23 @@ interface SystemInfo { export function fromVersion ( version: string, getSystemInfo: () => SystemInfo = () => ({ - get appVersion() { + get userAgent() { // @ts-ignore: browser code so must be skipped by ts - return window.navigator.appVersion + return window.navigator.userAgent } }) ): BoltAgent { const systemInfo = getSystemInfo() - //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = systemInfo.appVersion != null ? systemInfo.appVersion.split("(")[1].split(")")[0] : undefined + //USER_AGENT looks like 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' + + const platform = systemInfo.userAgent != null ? systemInfo.userAgent.split("(")[1].split(")")[0] : undefined + const languageDetails = systemInfo.userAgent || undefined return { product: `neo4j-javascript/${version}`, - platform: OS + platform, + languageDetails } } /* eslint-enable */ diff --git a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts index 3d57e2c90..4f882ae75 100644 --- a/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts +++ b/packages/core/test/internal/bolt-agent/browser/bolt-agent.test.ts @@ -22,9 +22,10 @@ describe('#unit boltAgent', () => { // This test is very fragile but the exact look of this string should not change without PM approval it('should return the correct bolt agent for specified version', () => { const version = '5.3' + const userAgent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' const getSystemInfo = (): any => { return { - appVersion: '5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36' + userAgent } } @@ -32,15 +33,16 @@ describe('#unit boltAgent', () => { expect(boltAgent).toEqual({ product: 'neo4j-javascript/5.3', - platform: 'Macintosh; Intel Mac OS X 10_15_7' + platform: 'Macintosh; Intel Mac OS X 10_15_7', + languageDetails: userAgent }) }) - it('should handle null appVersion', () => { + it('should handle null userAgent', () => { const version = '5.3' const getSystemInfo = (): any => { return { - appVersion: null + userAgent: null } } @@ -51,7 +53,7 @@ describe('#unit boltAgent', () => { }) }) - it('should handle undefined appVersion', () => { + it('should handle undefined userAgent', () => { const version = '5.3' const getSystemInfo = (): any => { return { diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts index b2d2a554c..ae29b1048 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/browser/bolt-agent.ts @@ -21,7 +21,7 @@ import { BoltAgent } from "../../../types"; interface SystemInfo { - appVersion?: string + userAgent?: string } /** @@ -34,20 +34,23 @@ interface SystemInfo { export function fromVersion ( version: string, getSystemInfo: () => SystemInfo = () => ({ - get appVersion() { + get userAgent() { // @ts-ignore: browser code so must be skipped by ts - return window.navigator.appVersion + return window.navigator.userAgent } }) ): BoltAgent { const systemInfo = getSystemInfo() - //APP_VERSION looks like 5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 - const OS = systemInfo.appVersion != null ? systemInfo.appVersion.split("(")[1].split(")")[0] : undefined + //USER_AGENT looks like 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' + + const platform = systemInfo.userAgent != null ? systemInfo.userAgent.split("(")[1].split(")")[0] : undefined + const languageDetails = systemInfo.userAgent || undefined return { product: `neo4j-javascript/${version}`, - platform: OS + platform, + languageDetails } } /* eslint-enable */ From 07969f6be75b14ed3f2eb99252feca124e430897 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 24 May 2023 16:44:36 +0200 Subject: [PATCH 16/18] Fix browser tests in the lite driver The basic browser tests in the lite driver is done by using a WebSocket implementation in a mocked browser. So, mocking the `window` is needed. --- .../test/integration/browser.environment.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/neo4j-driver-lite/test/integration/browser.environment.js b/packages/neo4j-driver-lite/test/integration/browser.environment.js index e62febeb0..ddea458c4 100644 --- a/packages/neo4j-driver-lite/test/integration/browser.environment.js +++ b/packages/neo4j-driver-lite/test/integration/browser.environment.js @@ -23,6 +23,11 @@ class BrowserEnvironment extends NodeEnvironment { async setup () { await super.setup() this.global.WebSocket = WebSocket + this.global.window = { + navigator: { + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' + } + } } async teardown () { From 1bf607b93f072077ce91711966bd293af5470279 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 24 May 2023 19:07:34 +0200 Subject: [PATCH 17/18] Remove `boltAgent` from API and fix declaration tests `boltAgent` is not configurable, so it should not be exposed in the `Config`. The `InternalConfig` interface was created for keeping the type-safety. The declaration tests were failing because of multiple `AbortSignal` definitons. This was happening because each project is using different versions of `@types/node` in the `package-lock.json`. The problem was not happening before because any interface exposed by the `neo4j-driver-core` was depending in Node defintions. The solution can be done or by remove the Node api reference or by pin the `@types/node` versions accross all packages. The first solution was choosed since the use of `string` instead `NodeJS.Platform` in the `SystemInfo` doesn't impact the usability of the `node.boltAgent.fromVersion` function. --- .../internal/bolt-agent/node/bolt-agent.ts | 2 +- packages/core/src/types.ts | 3 +++ .../internal/bolt-agent/node/bolt-agent.ts | 2 +- packages/neo4j-driver-deno/lib/core/types.ts | 3 +++ packages/neo4j-driver-deno/lib/mod.ts | 21 ++++++++++++------- packages/neo4j-driver-lite/src/index.ts | 21 ++++++++++++------- 6 files changed, 34 insertions(+), 18 deletions(-) diff --git a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts index e59921d12..53b619244 100644 --- a/packages/core/src/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/core/src/internal/bolt-agent/node/bolt-agent.ts @@ -23,7 +23,7 @@ interface SystemInfo { hostArch: string nodeVersion: string v8Version: string - platform: NodeJS.Platform + platform: string release: string } diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index b1c447e1e..b54125572 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -72,6 +72,9 @@ export interface Config { logging?: LoggingConfig resolver?: (address: string) => string[] | Promise userAgent?: string +} + +export interface InternalConfig extends Config { boltAgent?: BoltAgent } diff --git a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts index 28b605fdf..f8eb4e14d 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/bolt-agent/node/bolt-agent.ts @@ -23,7 +23,7 @@ interface SystemInfo { hostArch: string nodeVersion: string v8Version: string - platform: NodeJS.Platform + platform: string release: string } diff --git a/packages/neo4j-driver-deno/lib/core/types.ts b/packages/neo4j-driver-deno/lib/core/types.ts index b1c447e1e..b54125572 100644 --- a/packages/neo4j-driver-deno/lib/core/types.ts +++ b/packages/neo4j-driver-deno/lib/core/types.ts @@ -72,6 +72,9 @@ export interface Config { logging?: LoggingConfig resolver?: (address: string) => string[] | Promise userAgent?: string +} + +export interface InternalConfig extends Config { boltAgent?: BoltAgent } diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 339fbf4f6..767b40631 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -104,6 +104,7 @@ import { DirectConnectionProvider, RoutingConnectionProvider } from './bolt-conn type AuthToken = coreTypes.AuthToken type Config = coreTypes.Config +type InternalConfig = coreTypes.InternalConfig type TrustStrategy = coreTypes.TrustStrategy type EncryptionLevel = coreTypes.EncryptionLevel type SessionMode = coreTypes.SessionMode @@ -288,6 +289,9 @@ function driver ( assertString(url, 'Bolt URL') const parsedUrl = urlUtil.parseDatabaseUrl(url) + // enabling set boltAgent + const _config = config as unknown as InternalConfig + // Determine entryption/trust options from the URL. let routing = false let encrypted = false @@ -323,20 +327,21 @@ function driver ( // Encryption enabled on URL, propagate trust to the config. if (encrypted) { // Check for configuration conflict between URL and config. - if ('encrypted' in config || 'trust' in config) { + if ('encrypted' in _config || 'trust' in _config) { throw new Error( 'Encryption/trust can only be configured either through URL or config, not both' ) } - config.encrypted = ENCRYPTION_ON - config.trust = trust + _config.encrypted = ENCRYPTION_ON + _config.trust = trust } const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent ?? USER_AGENT - config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) + _config.userAgent = _config.userAgent ?? USER_AGENT + _config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) + const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -345,13 +350,13 @@ function driver ( routing } - return new Driver(meta, config, createConnectionProviderFunction()) + return new Driver(meta, _config, createConnectionProviderFunction()) function createConnectionProviderFunction (): (id: number, config: Config, log: Logger, hostNameResolver: ConfiguredCustomResolver) => ConnectionProvider { if (routing) { return ( id: number, - config: Config, + config: InternalConfig, log: Logger, hostNameResolver: ConfiguredCustomResolver ): ConnectionProvider => @@ -373,7 +378,7 @@ function driver ( ) } - return (id: number, config: Config, log: Logger): ConnectionProvider => + return (id: number, config: InternalConfig, log: Logger): ConnectionProvider => new DirectConnectionProvider({ id, config, diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 857c6e404..c168827d1 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -103,6 +103,7 @@ import { DirectConnectionProvider, RoutingConnectionProvider } from 'neo4j-drive type AuthToken = coreTypes.AuthToken type Config = coreTypes.Config +type InternalConfig = coreTypes.InternalConfig type TrustStrategy = coreTypes.TrustStrategy type EncryptionLevel = coreTypes.EncryptionLevel type SessionMode = coreTypes.SessionMode @@ -287,6 +288,9 @@ function driver ( assertString(url, 'Bolt URL') const parsedUrl = urlUtil.parseDatabaseUrl(url) + // enabling set boltAgent + const _config = config as unknown as InternalConfig + // Determine entryption/trust options from the URL. let routing = false let encrypted = false @@ -322,20 +326,21 @@ function driver ( // Encryption enabled on URL, propagate trust to the config. if (encrypted) { // Check for configuration conflict between URL and config. - if ('encrypted' in config || 'trust' in config) { + if ('encrypted' in _config || 'trust' in _config) { throw new Error( 'Encryption/trust can only be configured either through URL or config, not both' ) } - config.encrypted = ENCRYPTION_ON - config.trust = trust + _config.encrypted = ENCRYPTION_ON + _config.trust = trust } const authTokenManager = createAuthManager(authToken) // Use default user agent or user agent specified by user. - config.userAgent = config.userAgent ?? USER_AGENT - config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) + _config.userAgent = _config.userAgent ?? USER_AGENT + _config.boltAgent = internal.boltAgent.fromVersion('neo4j-javascript/' + VERSION) + const address = ServerAddress.fromUrl(parsedUrl.hostAndPort) const meta = { @@ -344,13 +349,13 @@ function driver ( routing } - return new Driver(meta, config, createConnectionProviderFunction()) + return new Driver(meta, _config, createConnectionProviderFunction()) function createConnectionProviderFunction (): (id: number, config: Config, log: Logger, hostNameResolver: ConfiguredCustomResolver) => ConnectionProvider { if (routing) { return ( id: number, - config: Config, + config: InternalConfig, log: Logger, hostNameResolver: ConfiguredCustomResolver ): ConnectionProvider => @@ -372,7 +377,7 @@ function driver ( ) } - return (id: number, config: Config, log: Logger): ConnectionProvider => + return (id: number, config: InternalConfig, log: Logger): ConnectionProvider => new DirectConnectionProvider({ id, config, From 0893c00d750e0a808664bbda61517b78e6d78225 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 25 May 2023 12:09:49 +0200 Subject: [PATCH 18/18] Fix browser test in Node10 environment The window mock was broken because it miss `Symbol`, `Date` and other global definitions. Set the window as `globalThis` and then define the navigator in it solves the issue. A proper way of solving the issue will be use `jsdom`, however this plugin doesn't work in Node10. Also fixed an issue with timers getting hanging in some situations. The issue was catched by browser test with `--detectOpenHandles`. --- .../bolt-connection/src/channel/browser/browser-channel.js | 2 +- .../lib/bolt-connection/channel/browser/browser-channel.js | 2 +- .../test/integration/browser.environment.js | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/bolt-connection/src/channel/browser/browser-channel.js b/packages/bolt-connection/src/channel/browser/browser-channel.js index ff5d1144e..c22e7fbb8 100644 --- a/packages/bolt-connection/src/channel/browser/browser-channel.js +++ b/packages/bolt-connection/src/channel/browser/browser-channel.js @@ -166,10 +166,10 @@ export default class WebSocketChannel { */ close () { return new Promise((resolve, reject) => { + this._clearConnectionTimeout() if (this._ws && this._ws.readyState !== WS_CLOSED) { this._open = false this.stopReceiveTimeout() - this._clearConnectionTimeout() this._ws.onclose = () => resolve() this._ws.close() } else { diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/channel/browser/browser-channel.js b/packages/neo4j-driver-deno/lib/bolt-connection/channel/browser/browser-channel.js index 655461e61..c38443e46 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/channel/browser/browser-channel.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/channel/browser/browser-channel.js @@ -166,10 +166,10 @@ export default class WebSocketChannel { */ close () { return new Promise((resolve, reject) => { + this._clearConnectionTimeout() if (this._ws && this._ws.readyState !== WS_CLOSED) { this._open = false this.stopReceiveTimeout() - this._clearConnectionTimeout() this._ws.onclose = () => resolve() this._ws.close() } else { diff --git a/packages/neo4j-driver-lite/test/integration/browser.environment.js b/packages/neo4j-driver-lite/test/integration/browser.environment.js index ddea458c4..8612ba539 100644 --- a/packages/neo4j-driver-lite/test/integration/browser.environment.js +++ b/packages/neo4j-driver-lite/test/integration/browser.environment.js @@ -23,10 +23,9 @@ class BrowserEnvironment extends NodeEnvironment { async setup () { await super.setup() this.global.WebSocket = WebSocket - this.global.window = { - navigator: { - userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' - } + this.global.window = globalThis + this.global.window.navigator = { + userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' } }