From e1911689a246767629cb27182ad0e78cf3ed5083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Barc=C3=A9los?= Date: Thu, 26 Oct 2023 21:30:44 +0200 Subject: [PATCH] Introduce explicity resource management to Driver and Session objects (#1154) This is a TC39 [proposal](https://github.com/tc39/proposal-explicit-resource-management) which is already implemented in Typescript 5.2, core-js, babel and other polyfill tools. This feature enables the user create the driver or a session with the `await using` keywords and then do not have to close the resource afterwards, since this resources will be closed after leaving the block which were created at. For example: ```typescript await using driver = neo4j.driver(uri, authToken) await using session = driver.session() await session.executeRead(tx => "RETURN 1") ``` Since Deno is more strict with typescript, small fixes had to be done in the driver. * Add a value to Result[Symbol.toStringTag]. * Fix Integer.shiftRight function to add proper integer conversion it. A package.json file was added to the `neo4j-driver-deno` folder for making easier to integrate the test runners to the environments. --- package.json | 4 +- packages/core/src/driver.ts | 6 +++ packages/core/src/integer.ts | 3 +- packages/core/src/result.ts | 2 +- packages/core/src/session.ts | 6 +++ packages/neo4j-driver-deno/lib/core/driver.ts | 6 +++ .../neo4j-driver-deno/lib/core/integer.ts | 3 +- packages/neo4j-driver-deno/lib/core/result.ts | 2 +- .../neo4j-driver-deno/lib/core/session.ts | 6 +++ packages/neo4j-driver-deno/package.json | 19 ++++++++ packages/neo4j-driver-deno/test/neo4j.test.ts | 44 +++++++++++++++++++ packages/neo4j-driver/src/session-rx.js | 4 ++ packages/testkit-backend/package.json | 2 +- testkit/Dockerfile | 2 +- testkit/build.py | 2 +- 15 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 packages/neo4j-driver-deno/package.json create mode 100644 packages/neo4j-driver-deno/test/neo4j.test.ts diff --git a/package.json b/package.json index 76daeced5..63293b227 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "scripts": { "clean": "lerna clean -y && lerna run clean", "build": "lerna bootstrap --ci", - "set_version::deno": "cd ./packages/neo4j-driver-deno && deno run --allow-read --allow-write ./versioning.ts --output=. --filename=current.version.ts", - "build::deno": "cd ./packages/neo4j-driver-deno && deno run --allow-read --allow-write --allow-net ./generate.ts", + "set_version::deno": "lerna run set_version --scope neo4j-driver-deno --stream -- ", + "build::deno": "lerna run build --scope neo4j-driver-deno --stream -- ", "build::notci": "lerna bootstrap", "docs": "lerna run docs --stream --concurrency 1", "test::unit": "lerna run test::unit --stream", diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 9f2a7a61e..09f3353bc 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -776,6 +776,12 @@ class Driver { return Promise.resolve() } + // eslint-disable-next-line + // @ts-ignore + [Symbol.asyncDispose] (): Promise { + return this.close() + } + /** * @protected * @returns {void} diff --git a/packages/core/src/integer.ts b/packages/core/src/integer.ts index bbb8d4d03..e9fd323bb 100644 --- a/packages/core/src/integer.ts +++ b/packages/core/src/integer.ts @@ -695,10 +695,11 @@ class Integer { */ shiftRight (numBits: number | Integer): Integer { let bitsCount: number = Integer.toNumber(numBits) + const numBitNum: number = Integer.toNumber(numBits) if ((bitsCount &= 63) === 0) { return Integer.ZERO - } else if (numBits < 32) { + } else if (numBitNum < 32) { return Integer.fromBits( (this.low >>> bitsCount) | (this.high << (32 - bitsCount)), this.high >> bitsCount diff --git a/packages/core/src/result.ts b/packages/core/src/result.ts index 1ef7771d2..938f04ff1 100644 --- a/packages/core/src/result.ts +++ b/packages/core/src/result.ts @@ -375,7 +375,7 @@ class Result implements Promise void) | null): Promise> { return this._getOrCreatePromise().finally(onfinally) } diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 2fad4f2d7..bc10af73f 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -561,6 +561,12 @@ class Session { } } + // eslint-disable-next-line + // @ts-ignore + [Symbol.asyncDispose] (): Promise { + return this.close() + } + _connectionHolderWithMode (mode: SessionMode): ConnectionHolder { if (mode === ACCESS_MODE_READ) { return this._readConnectionHolder diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 61a02a5b5..e151bde47 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -776,6 +776,12 @@ class Driver { return Promise.resolve() } + // eslint-disable-next-line + // @ts-ignore + [Symbol.asyncDispose] (): Promise { + return this.close() + } + /** * @protected * @returns {void} diff --git a/packages/neo4j-driver-deno/lib/core/integer.ts b/packages/neo4j-driver-deno/lib/core/integer.ts index 0b4afd57c..540fe4db1 100644 --- a/packages/neo4j-driver-deno/lib/core/integer.ts +++ b/packages/neo4j-driver-deno/lib/core/integer.ts @@ -695,10 +695,11 @@ class Integer { */ shiftRight (numBits: number | Integer): Integer { let bitsCount: number = Integer.toNumber(numBits) + const numBitNum: number = Integer.toNumber(numBits) if ((bitsCount &= 63) === 0) { return Integer.ZERO - } else if (numBits < 32) { + } else if (numBitNum < 32) { return Integer.fromBits( (this.low >>> bitsCount) | (this.high << (32 - bitsCount)), this.high >> bitsCount diff --git a/packages/neo4j-driver-deno/lib/core/result.ts b/packages/neo4j-driver-deno/lib/core/result.ts index f000b6ac1..43ba35dfe 100644 --- a/packages/neo4j-driver-deno/lib/core/result.ts +++ b/packages/neo4j-driver-deno/lib/core/result.ts @@ -375,7 +375,7 @@ class Result implements Promise void) | null): Promise> { return this._getOrCreatePromise().finally(onfinally) } diff --git a/packages/neo4j-driver-deno/lib/core/session.ts b/packages/neo4j-driver-deno/lib/core/session.ts index 3e0626a1e..5c65570c9 100644 --- a/packages/neo4j-driver-deno/lib/core/session.ts +++ b/packages/neo4j-driver-deno/lib/core/session.ts @@ -561,6 +561,12 @@ class Session { } } + // eslint-disable-next-line + // @ts-ignore + [Symbol.asyncDispose] (): Promise { + return this.close() + } + _connectionHolderWithMode (mode: SessionMode): ConnectionHolder { if (mode === ACCESS_MODE_READ) { return this._readConnectionHolder diff --git a/packages/neo4j-driver-deno/package.json b/packages/neo4j-driver-deno/package.json new file mode 100644 index 000000000..69ed1462f --- /dev/null +++ b/packages/neo4j-driver-deno/package.json @@ -0,0 +1,19 @@ +{ + "name": "neo4j-driver-deno", + "version": "5.0.0-dev", + "description": "Package just used for running scripts", + "private": true, + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "npm run test::integration", + "test::integration": "deno test --allow-all ./test", + "set_version": "deno run --allow-read --allow-write ./versioning.ts --output=. --filename=current.version.ts", + "build": "deno run --allow-read --allow-write --allow-net ./generate.ts" + }, + "author": "Neo4j", + "license": "Apache-2.0" +} diff --git a/packages/neo4j-driver-deno/test/neo4j.test.ts b/packages/neo4j-driver-deno/test/neo4j.test.ts new file mode 100644 index 000000000..2bc4f4fff --- /dev/null +++ b/packages/neo4j-driver-deno/test/neo4j.test.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 neo4j from '../lib/mod.ts' + +const env = Deno.env.toObject() + +const username = env.TEST_NEO4J_USER || 'neo4j' +const password = env.TEST_NEO4J_PASS || 'password' +const hostname = env.TEST_NEO4J_HOST || 'localhost' +const scheme = env.TEST_NEO4J_SCHEME || 'bolt' +const boltPort = env.TEST_NEO4J_BOLT_PORT || 7687 +const uri = `${scheme}://${hostname}:${boltPort}` +const authToken = neo4j.auth.basic(username, password) + +// Deno will fail with resource leaks +Deno.test('neo4j.driver should be able to use explicity resource management', async () => { + await using driver = neo4j.driver(uri, authToken) + + await driver.executeQuery('RETURN 1') +}) + +// Deno will fail with resource leaks +Deno.test('driver.session should be able to use explicity resource management', async () => { + await using driver = neo4j.driver(uri, authToken) + await using session = driver.session() + + await session.executeRead(tx => "RETURN 1") +}) diff --git a/packages/neo4j-driver/src/session-rx.js b/packages/neo4j-driver/src/session-rx.js index 1cdef102a..d5739e8f4 100644 --- a/packages/neo4j-driver/src/session-rx.js +++ b/packages/neo4j-driver/src/session-rx.js @@ -163,6 +163,10 @@ export default class RxSession { }) } + [Symbol.asyncDispose] () { + return this.close() + } + /** * Returns the bookmarks received following the last successfully completed query, which is executed * either in an {@link RxTransaction} obtained from this session instance or directly through one of diff --git a/packages/testkit-backend/package.json b/packages/testkit-backend/package.json index 09b0fe41e..45ae6e92b 100644 --- a/packages/testkit-backend/package.json +++ b/packages/testkit-backend/package.json @@ -12,7 +12,7 @@ "scripts": { "build": "rollup src/index.js --config rollup.config.js", "start": "node --version | grep -q v10. && node -r esm src/index.js || node --experimental-specifier-resolution=node src/index.js", - "start::deno": "deno run --allow-read --allow-write --allow-net --allow-env --allow-run deno/index.ts", + "start::deno": "deno run --allow-read --allow-write --allow-net --allow-env --allow-sys --allow-run deno/index.ts", "clean": "rm -fr node_modules public/index.js", "prepare": "npm run build", "node": "node" diff --git a/testkit/Dockerfile b/testkit/Dockerfile index 15e30041a..4fdbdbd96 100644 --- a/testkit/Dockerfile +++ b/testkit/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:20.04 ARG NODE_VERSION=10 -ARG DENO_VERSION=1.19.3 +ARG DENO_VERSION=1.37.2 ENV DEBIAN_FRONTEND noninteractive ENV NODE_OPTIONS --max_old_space_size=4096 --use-openssl-ca diff --git a/testkit/build.py b/testkit/build.py index 7fdbd7000..2776b9259 100644 --- a/testkit/build.py +++ b/testkit/build.py @@ -19,7 +19,7 @@ def init_monorepo(): def clean_and_build(): run_in_driver_repo(["npm", "run", "clean"], env=os.environ) run_in_driver_repo(["npm", "run", "build"], env=os.environ) - run_in_driver_repo(["npm", "run", "build::deno", "--", + run_in_driver_repo(["npm", "run", "build::deno", "--", "--", "--output=lib2/"], env=os.environ) if is_deno() and is_team_city():