From 460c44f0952f2ace380e9685fadc628dee2b3638 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 25 Oct 2022 17:31:06 +0200 Subject: [PATCH 1/4] Add generics and type mapping to Result, Session.run and Transaction.run The new generic typing allow mapping the records of these running queries to type-safe Record. Given the following Person and Friendship definitions. ```typescript interface Person { age: Integer name: string } interface Friendship { since: Integer } interface PersonAndFriendship { p: Node f: Relationship } ``` The new type-mapping allow safe access the properties of query which return `Record` ```typescript const { records } = await session.run('MATCH (p:Person) RETURN p.name, p.age') for (const person of records) { const age: Integer = person.get('age') const name: string = person.get('name') // @ts-expect-error const nameInt: Integer = person.get('name') } ``` The type-mapping can be also extended for `Node` and `Relationship`. ```typescript const { records } = await session.run('MATCH (p:Person)-[f:Friendship]-() RETURN p, f') for (const r of records) { const person = r.get('p') const age: Integer = person.properties.age const name: string = person.properties.name // @ts-expect-error const nameInt: Integer = person.properties.name // @ts-expect-error const err: string = person.properties.err const friendship = r.get('f') const since: Integer = friendship.properties.since // @ts-expect-error const sinceString: string = friendship.properties.since // @ts-expect-error const err2: string = friendship.properties.err } ``` The usage in combination with `executeRead` and `executeWrite` is also possibile. ``` const { records } = await session.executeRead(tx => tx.run('MATCH (p:Person) RETURN p.name, p.age')) for (const person of records) { const age: Integer = person.get('age') const name: string = person.get('name') // @ts-expect-error const nameInt: Integer = person.get('name') } ``` --- packages/core/src/record.ts | 1 + packages/core/src/result.ts | 22 +++--- packages/core/src/session.ts | 5 +- packages/core/src/transaction-managed.ts | 3 +- packages/core/src/transaction.ts | 5 +- packages/neo4j-driver-deno/lib/core/record.ts | 1 + packages/neo4j-driver-deno/lib/core/result.ts | 22 +++--- .../neo4j-driver-deno/lib/core/session.ts | 5 +- .../lib/core/transaction-managed.ts | 3 +- .../neo4j-driver-deno/lib/core/transaction.ts | 5 +- .../test/types/graph-types.test.ts | 23 ++++++ .../neo4j-driver/test/types/session.test.ts | 79 ++++++++++++++++++- .../test/types/transaction.test.ts | 56 ++++++++++++- 13 files changed, 196 insertions(+), 34 deletions(-) diff --git a/packages/core/src/record.ts b/packages/core/src/record.ts index 54042997f..21198436e 100644 --- a/packages/core/src/record.ts +++ b/packages/core/src/record.ts @@ -243,3 +243,4 @@ class Record< } export default Record +export type { Dict } diff --git a/packages/core/src/result.ts b/packages/core/src/result.ts index bc2bac72f..7f79d5e5d 100644 --- a/packages/core/src/result.ts +++ b/packages/core/src/result.ts @@ -20,7 +20,7 @@ /* eslint-disable @typescript-eslint/promise-function-async */ import ResultSummary from './result-summary' -import Record from './record' +import Record, { Dict } from './record' import { Query, PeekableAsyncIterator } from './types' import { observer, util, connectionHolder } from './internal' import { newError, PROTOCOL_ERROR } from './error' @@ -56,8 +56,8 @@ const DEFAULT_ON_KEYS = (keys: string[]): void => {} * The query result is the combination of the {@link ResultSummary} and * the array {@link Record[]} produced by the query */ -interface QueryResult { - records: Record[] +interface QueryResult { + records: Array> summary: ResultSummary } @@ -111,7 +111,7 @@ interface QueuedResultObserver extends ResultObserver { * Alternatively can be consumed lazily using {@link Result#subscribe} function. * @access public */ -class Result implements Promise { +class Result implements Promise> { private readonly _stack: string | null private readonly _streamObserverPromise: Promise private _p: Promise | null @@ -212,7 +212,7 @@ class Result implements Promise { * @private * @return {Promise} new Promise. */ - private _getOrCreatePromise (): Promise { + private _getOrCreatePromise (): Promise> { if (this._p == null) { this._p = new Promise((resolve, reject) => { const records: Record[] = [] @@ -240,9 +240,9 @@ class Result implements Promise { * *Should not be combined with {@link Result#subscribe} or ${@link Result#then} functions.* * * @public - * @returns {PeekableAsyncIterator} The async iterator for the Results + * @returns {PeekableAsyncIterator, ResultSummary>} The async iterator for the Results */ - [Symbol.asyncIterator] (): PeekableAsyncIterator { + [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { if (!this.isOpen()) { const error = newError('Result is already consumed') return { @@ -345,9 +345,9 @@ class Result implements Promise { * @param {function(error: {message:string, code:string})} onRejected - function to be called upon errors. * @return {Promise} promise. */ - then( + then, TResult2 = never>( onFulfilled?: - | ((value: QueryResult) => TResult1 | PromiseLike) + | ((value: QueryResult) => TResult1 | PromiseLike) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike) | null ): Promise { @@ -364,7 +364,7 @@ class Result implements Promise { */ catch ( onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise { + ): Promise | TResult> { return this._getOrCreatePromise().catch(onRejected) } @@ -376,7 +376,7 @@ class Result implements Promise { * @return {Promise} promise. */ [Symbol.toStringTag]: string - finally (onfinally?: (() => void) | null): Promise { + finally (onfinally?: (() => void) | null): Promise> { return this._getOrCreatePromise().finally(onfinally) } diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index dfa588d3f..73cd24e81 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -36,6 +36,7 @@ import { NumberOrInteger } from './graph-types' import TransactionPromise from './transaction-promise' import ManagedTransaction from './transaction-managed' import BookmarkManager from './bookmark-manager' +import { Dict } from './record' type ConnectionConsumer = (connection: Connection | null) => any | undefined | Promise | Promise type TransactionWork = (tx: Transaction) => Promise | T @@ -154,11 +155,11 @@ class Session { * @param {TransactionConfig} [transactionConfig] - Configuration for the new auto-commit transaction. * @return {Result} New Result. */ - run ( + run ( query: Query, parameters?: any, transactionConfig?: TransactionConfig - ): Result { + ): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/core/src/transaction-managed.ts b/packages/core/src/transaction-managed.ts index 0bd566f82..6efbcc704 100644 --- a/packages/core/src/transaction-managed.ts +++ b/packages/core/src/transaction-managed.ts @@ -20,6 +20,7 @@ import Result from './result' import Transaction from './transaction' import { Query } from './types' +import { Dict } from './record' type Run = (query: Query, parameters?: any) => Result @@ -60,7 +61,7 @@ class ManagedTransaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { return this._run(query, parameters) } } diff --git a/packages/core/src/transaction.ts b/packages/core/src/transaction.ts index 57b775332..054df751d 100644 --- a/packages/core/src/transaction.ts +++ b/packages/core/src/transaction.ts @@ -37,6 +37,7 @@ import { import { newError } from './error' import Result from './result' import { Query } from './types' +import { Dict } from './record' /** * Represents a transaction in the Neo4j database. @@ -109,7 +110,7 @@ class Transaction { this._lowRecordWatermak = lowRecordWatermark this._highRecordWatermark = highRecordWatermark this._bookmarks = Bookmarks.empty() - this._acceptActive = () => { } // satisfy DenoJS + this._acceptActive = () => { } // satisfy DenoJS this._activePromise = new Promise((resolve, reject) => { this._acceptActive = resolve }) @@ -174,7 +175,7 @@ class Transaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/neo4j-driver-deno/lib/core/record.ts b/packages/neo4j-driver-deno/lib/core/record.ts index 759cbf821..dfdee2ee0 100644 --- a/packages/neo4j-driver-deno/lib/core/record.ts +++ b/packages/neo4j-driver-deno/lib/core/record.ts @@ -243,3 +243,4 @@ class Record< } export default Record +export type { Dict } diff --git a/packages/neo4j-driver-deno/lib/core/result.ts b/packages/neo4j-driver-deno/lib/core/result.ts index e2e380aaa..71a4ade51 100644 --- a/packages/neo4j-driver-deno/lib/core/result.ts +++ b/packages/neo4j-driver-deno/lib/core/result.ts @@ -20,7 +20,7 @@ /* eslint-disable @typescript-eslint/promise-function-async */ import ResultSummary from './result-summary.ts' -import Record from './record.ts' +import Record, { Dict } from './record.ts' import { Query, PeekableAsyncIterator } from './types.ts' import { observer, util, connectionHolder } from './internal/index.ts' import { newError, PROTOCOL_ERROR } from './error.ts' @@ -56,8 +56,8 @@ const DEFAULT_ON_KEYS = (keys: string[]): void => {} * The query result is the combination of the {@link ResultSummary} and * the array {@link Record[]} produced by the query */ -interface QueryResult { - records: Record[] +interface QueryResult { + records: Array> summary: ResultSummary } @@ -111,7 +111,7 @@ interface QueuedResultObserver extends ResultObserver { * Alternatively can be consumed lazily using {@link Result#subscribe} function. * @access public */ -class Result implements Promise { +class Result implements Promise> { private readonly _stack: string | null private readonly _streamObserverPromise: Promise private _p: Promise | null @@ -212,7 +212,7 @@ class Result implements Promise { * @private * @return {Promise} new Promise. */ - private _getOrCreatePromise (): Promise { + private _getOrCreatePromise (): Promise> { if (this._p == null) { this._p = new Promise((resolve, reject) => { const records: Record[] = [] @@ -240,9 +240,9 @@ class Result implements Promise { * *Should not be combined with {@link Result#subscribe} or ${@link Result#then} functions.* * * @public - * @returns {PeekableAsyncIterator} The async iterator for the Results + * @returns {PeekableAsyncIterator, ResultSummary>} The async iterator for the Results */ - [Symbol.asyncIterator] (): PeekableAsyncIterator { + [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { if (!this.isOpen()) { const error = newError('Result is already consumed') return { @@ -345,9 +345,9 @@ class Result implements Promise { * @param {function(error: {message:string, code:string})} onRejected - function to be called upon errors. * @return {Promise} promise. */ - then( + then, TResult2 = never>( onFulfilled?: - | ((value: QueryResult) => TResult1 | PromiseLike) + | ((value: QueryResult) => TResult1 | PromiseLike) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike) | null ): Promise { @@ -364,7 +364,7 @@ class Result implements Promise { */ catch ( onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise { + ): Promise | TResult> { return this._getOrCreatePromise().catch(onRejected) } @@ -376,7 +376,7 @@ class Result implements Promise { * @return {Promise} promise. */ [Symbol.toStringTag]: string - finally (onfinally?: (() => void) | null): Promise { + finally (onfinally?: (() => 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 6a419e201..952089802 100644 --- a/packages/neo4j-driver-deno/lib/core/session.ts +++ b/packages/neo4j-driver-deno/lib/core/session.ts @@ -36,6 +36,7 @@ import { NumberOrInteger } from './graph-types.ts' import TransactionPromise from './transaction-promise.ts' import ManagedTransaction from './transaction-managed.ts' import BookmarkManager from './bookmark-manager.ts' +import { Dict } from './record.ts' type ConnectionConsumer = (connection: Connection | null) => any | undefined | Promise | Promise type TransactionWork = (tx: Transaction) => Promise | T @@ -154,11 +155,11 @@ class Session { * @param {TransactionConfig} [transactionConfig] - Configuration for the new auto-commit transaction. * @return {Result} New Result. */ - run ( + run ( query: Query, parameters?: any, transactionConfig?: TransactionConfig - ): Result { + ): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/neo4j-driver-deno/lib/core/transaction-managed.ts b/packages/neo4j-driver-deno/lib/core/transaction-managed.ts index 162af33c9..f0541aaa2 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction-managed.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction-managed.ts @@ -20,6 +20,7 @@ import Result from './result.ts' import Transaction from './transaction.ts' import { Query } from './types.ts' +import { Dict } from './record.ts' type Run = (query: Query, parameters?: any) => Result @@ -60,7 +61,7 @@ class ManagedTransaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { return this._run(query, parameters) } } diff --git a/packages/neo4j-driver-deno/lib/core/transaction.ts b/packages/neo4j-driver-deno/lib/core/transaction.ts index 66c4c22e1..31af071db 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction.ts @@ -37,6 +37,7 @@ import { import { newError } from './error.ts' import Result from './result.ts' import { Query } from './types.ts' +import { Dict } from './record.ts' /** * Represents a transaction in the Neo4j database. @@ -109,7 +110,7 @@ class Transaction { this._lowRecordWatermak = lowRecordWatermark this._highRecordWatermark = highRecordWatermark this._bookmarks = Bookmarks.empty() - this._acceptActive = () => { } // satisfy DenoJS + this._acceptActive = () => { } // satisfy DenoJS this._activePromise = new Promise((resolve, reject) => { this._acceptActive = resolve }) @@ -174,7 +175,7 @@ class Transaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/neo4j-driver/test/types/graph-types.test.ts b/packages/neo4j-driver/test/types/graph-types.test.ts index 2e16a62b5..ce730e4e9 100644 --- a/packages/neo4j-driver/test/types/graph-types.test.ts +++ b/packages/neo4j-driver/test/types/graph-types.test.ts @@ -163,3 +163,26 @@ const path2Start: Node = path2.start const path2End: Node = path2.end const path2Segments: Array> = path2.segments const isPath2: boolean = path2 instanceof Path + +interface Person { + age: number + name: string +} + +const dummy: any = null + +{ + const personNode: Node = dummy + const age: number = personNode.properties.age + const name: string = personNode.properties.name + // @ts-expect-error + const nameInt: number = personNode.properties.name +} + +{ + const personRel: Relationship = dummy + const age: number = personRel.properties.age + const name: string = personRel.properties.name + // @ts-expect-error + const nameInt: number = personRel.properties.name +} diff --git a/packages/neo4j-driver/test/types/session.test.ts b/packages/neo4j-driver/test/types/session.test.ts index df3534465..0d7303cce 100644 --- a/packages/neo4j-driver/test/types/session.test.ts +++ b/packages/neo4j-driver/test/types/session.test.ts @@ -27,7 +27,9 @@ import { Result, Transaction, Session, - TransactionConfig + TransactionConfig, + Node, + Relationship } from 'neo4j-driver-core' const dummy: any = null @@ -176,3 +178,78 @@ const promise6: Promise = session.writeTransaction( ) const lastBookmarks: string[] = session.lastBookmarks() + +interface Person { + age: Integer + name: string +} + +interface Friendship { + since: Integer +} + +interface PersonAndFriendship { + p: Node + f: Relationship +} + +const personSessionRun = session.run('MATCH (p:Person) RETURN p.name, p.age') +personSessionRun.then(({ records }) => { + for (const person of records) { + const age: Integer = person.get('age') + const name: string = person.get('name') + + // @ts-expect-error + const nameInt: Integer = person.get('name') + } +}).catch(err => console.error(err)) + +const personAndFriend = session.run('MATCH (p:Person)-[f:Friendship]-() RETURN p, f') +personAndFriend.then(({ records }) => { + for (const r of records) { + const person = r.get('p') + + const age: Integer = person.properties.age + const name: string = person.properties.name + + // @ts-expect-error + const nameInt: Integer = person.properties.name + + // @ts-expect-error + const err: string = person.properties.err + + const friendship = r.get('f') + + const since: Integer = friendship.properties.since + + // @ts-expect-error + const sinceString: string = friendship.properties.since + + // @ts-expect-error + const err2: string = friendship.properties.err + } +}).catch(error => console.error(error)) + +const personExecuteRead = session.executeRead(tx => tx.run('MATCH (p:Person) RETURN p.name, p.age')) + +personExecuteRead.then(({ records }) => { + for (const person of records) { + const age: Integer = person.get('age') + const name: string = person.get('name') + + // @ts-expect-error + const nameInt: Integer = person.get('name') + } +}).catch(err => console.error(err)) + +const personExecuteWrite = session.executeWrite(tx => tx.run('MATCH (p:Person) RETURN p.name, p.age')) + +personExecuteWrite.then(({ records }) => { + for (const person of records) { + const age: Integer = person.get('age') + const name: string = person.get('name') + + // @ts-expect-error + const nameInt: Integer = person.get('name') + } +}).catch(err => console.error(err)) diff --git a/packages/neo4j-driver/test/types/transaction.test.ts b/packages/neo4j-driver/test/types/transaction.test.ts index 2119199a2..e8224be27 100644 --- a/packages/neo4j-driver/test/types/transaction.test.ts +++ b/packages/neo4j-driver/test/types/transaction.test.ts @@ -24,7 +24,10 @@ import { ResultSummary, Result, QueryResult, - Transaction + Transaction, + Integer, + Node, + Relationship } from 'neo4j-driver-core' const dummy: any = null @@ -95,3 +98,54 @@ tx.commit().then(() => { tx.rollback().then(() => { console.log('transaction rolled back') }).catch(error => console.error(error)) + +interface Person { + age: Integer + name: string +} + +interface Friendship { + since: Integer +} + +interface PersonAndFriendship { + p: Node + f: Relationship +} + +const personTxRun = tx.run('MATCH (p:Person) RETURN p.name, p.age') +personTxRun.then(({ records }) => { + for (const person of records) { + const age: Integer = person.get('age') + const name: string = person.get('name') + + // @ts-expect-error + const nameInt: Integer = person.get('name') + } +}).catch(error => console.error(error)) + +const personAndFriend = tx.run('MATCH (p:Person)-[f:Friendship]-() RETURN p, f') +personAndFriend.then(({ records }) => { + for (const r of records) { + const person = r.get('p') + + const age: Integer = person.properties.age + const name: string = person.properties.name + + // @ts-expect-error + const nameInt: Integer = person.properties.name + + // @ts-expect-error + const err: string = person.properties.err + + const friendship = r.get('f') + + const since: Integer = friendship.properties.since + + // @ts-expect-error + const sinceString: string = friendship.properties.since + + // @ts-expect-error + const err2: string = friendship.properties.err + } +}).catch(error => console.error(error)) From 65a86042f31878a0526ff4d43a43c8c8118de76f Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 25 Oct 2022 18:46:33 +0200 Subject: [PATCH 2/4] Add type-safety to Result.subscribe --- packages/core/src/result.ts | 6 +- packages/core/test/result.test.ts | 100 ++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/packages/core/src/result.ts b/packages/core/src/result.ts index 7f79d5e5d..ba053248f 100644 --- a/packages/core/src/result.ts +++ b/packages/core/src/result.ts @@ -65,7 +65,7 @@ interface QueryResult { * Interface to observe updates on the Result which is being produced. * */ -interface ResultObserver { +interface ResultObserver { /** * Receive the keys present on the record whenever this information is available * @@ -77,7 +77,7 @@ interface ResultObserver { * Receive the each record present on the {@link @Result} * @param {Record} record The {@link Record} produced */ - onNext?: (record: Record) => void + onNext?: (record: Record) => void /** * Called when the result is fully received @@ -391,7 +391,7 @@ class Result implements Promise): void { this._subscribe(observer) .catch(() => {}) } diff --git a/packages/core/test/result.test.ts b/packages/core/test/result.test.ts index acb76d396..93077d7f1 100644 --- a/packages/core/test/result.test.ts +++ b/packages/core/test/result.test.ts @@ -28,6 +28,11 @@ import { import Result from '../src/result' import FakeConnection from './utils/connection.fake' +interface AB { + a: number + b: number +} + describe('Result', () => { const expectedError = newError('some error') @@ -305,6 +310,34 @@ describe('Result', () => { ]) }) + it('should redirect onNext to the client observer with type safety', async () => { + const result = new Result(Promise.resolve(streamObserverMock), 'query') + + const keys = ['a', 'b'] + const rawRecord1 = [1, 2] + const rawRecord2 = [3, 4] + const receivedRecords: Array<[number, number]> = [] + + streamObserverMock.onKeys(keys) + streamObserverMock.onNext(rawRecord1) + streamObserverMock.onNext(rawRecord2) + + await result.subscribe({ + onNext (record) { + const a: number = record.get('a') + const b: number = record.get('b') + + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _: string = record.get('a') + + receivedRecords.push([a, b]) + } + }) + + expect(receivedRecords).toEqual([rawRecord1, rawRecord2]) + }) + describe.each([ ['query', {}, { query: 'query', parameters: {} }], ['query', { a: 1 }, { query: 'query', parameters: { a: 1 } }], @@ -540,6 +573,45 @@ describe('Result', () => { new Record(keys, rawRecord2) ]) }) + + it('should resolve with summary and records type safety', async () => { + const result = new Result(Promise.resolve(streamObserverMock), expected.query, expected.parameters) + const metadata = { + resultConsumedAfter: 20, + resultAvailableAfter: 124, + extraInfo: 'extra' + } + const expectedSummary = new ResultSummary( + expected.query, + expected.parameters, + metadata + ) + const keys = ['a', 'b'] + const rawRecord1 = [1, 2] + const rawRecord2 = [3, 4] + + streamObserverMock.onKeys(keys) + streamObserverMock.onNext(rawRecord1) + streamObserverMock.onNext(rawRecord2) + + streamObserverMock.onCompleted(metadata) + + const { summary, records } = await result + + const rawRecords = records.map(record => { + const a: number = record.get('a') + const b: number = record.get('b') + + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _: string = record.get('a') + + return [a, b] + }) + + expect(summary).toEqual(expectedSummary) + expect(rawRecords).toEqual([rawRecord1, rawRecord2]) + }) }) it('should reject promise with the occurred error and new stacktrace', done => { @@ -808,6 +880,34 @@ describe('Result', () => { ]) }) + it('should iterate over record with type safety', async () => { + const result = new Result(Promise.resolve(streamObserverMock), 'query') + + const keys = ['a', 'b'] + const rawRecord1 = [1, 2] + const rawRecord2 = [3, 4] + + streamObserverMock.onKeys(keys) + streamObserverMock.onNext(rawRecord1) + streamObserverMock.onNext(rawRecord2) + + streamObserverMock.onCompleted({}) + + const receivedRawRecords = [] + for await (const record of result) { + const a: number = record.get('a') + const b: number = record.get('b') + + // @ts-expect-error + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const _: string = record.get('a') + + receivedRawRecords.push([a, b]) + } + + expect(receivedRawRecords).toEqual([rawRecord1, rawRecord2]) + }) + it('should return summary when it finishes', async () => { const keys = ['a', 'b'] const rawRecord1 = [1, 2] From 9d7c128328e54d5edbc546208d8d6fcb8bc76db4 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 25 Oct 2022 18:54:10 +0200 Subject: [PATCH 3/4] more tests, update deno --- packages/neo4j-driver-deno/lib/core/result.ts | 6 +++--- packages/neo4j-driver/test/types/session.test.ts | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/neo4j-driver-deno/lib/core/result.ts b/packages/neo4j-driver-deno/lib/core/result.ts index 71a4ade51..66a1f19e4 100644 --- a/packages/neo4j-driver-deno/lib/core/result.ts +++ b/packages/neo4j-driver-deno/lib/core/result.ts @@ -65,7 +65,7 @@ interface QueryResult { * Interface to observe updates on the Result which is being produced. * */ -interface ResultObserver { +interface ResultObserver { /** * Receive the keys present on the record whenever this information is available * @@ -77,7 +77,7 @@ interface ResultObserver { * Receive the each record present on the {@link @Result} * @param {Record} record The {@link Record} produced */ - onNext?: (record: Record) => void + onNext?: (record: Record) => void /** * Called when the result is fully received @@ -391,7 +391,7 @@ class Result implements Promise): void { this._subscribe(observer) .catch(() => {}) } diff --git a/packages/neo4j-driver/test/types/session.test.ts b/packages/neo4j-driver/test/types/session.test.ts index 0d7303cce..2a34a2d6e 100644 --- a/packages/neo4j-driver/test/types/session.test.ts +++ b/packages/neo4j-driver/test/types/session.test.ts @@ -234,11 +234,16 @@ const personExecuteRead = session.executeRead(tx => tx.run('MATCH (p:Per personExecuteRead.then(({ records }) => { for (const person of records) { - const age: Integer = person.get('age') - const name: string = person.get('name') + let age: Integer = person.get('age') + let name: string = person.get('name') // @ts-expect-error const nameInt: Integer = person.get('name') + + const p = person.toObject() + + age = p.age + name = p.name } }).catch(err => console.error(err)) From b5e8c60aa78ae788cc6e2d01224b5302aabc2e87 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 26 Oct 2022 16:03:51 +0200 Subject: [PATCH 4/4] Rename Entries to RecordShape --- packages/core/src/result.ts | 26 +++++++++---------- packages/core/src/session.ts | 4 +-- packages/core/src/transaction-managed.ts | 2 +- packages/core/src/transaction.ts | 2 +- packages/neo4j-driver-deno/lib/core/result.ts | 26 +++++++++---------- .../neo4j-driver-deno/lib/core/session.ts | 4 +-- .../lib/core/transaction-managed.ts | 2 +- .../neo4j-driver-deno/lib/core/transaction.ts | 2 +- 8 files changed, 34 insertions(+), 34 deletions(-) diff --git a/packages/core/src/result.ts b/packages/core/src/result.ts index ba053248f..3e050a06d 100644 --- a/packages/core/src/result.ts +++ b/packages/core/src/result.ts @@ -56,8 +56,8 @@ const DEFAULT_ON_KEYS = (keys: string[]): void => {} * The query result is the combination of the {@link ResultSummary} and * the array {@link Record[]} produced by the query */ -interface QueryResult { - records: Array> +interface QueryResult { + records: Array> summary: ResultSummary } @@ -65,7 +65,7 @@ interface QueryResult { * Interface to observe updates on the Result which is being produced. * */ -interface ResultObserver { +interface ResultObserver { /** * Receive the keys present on the record whenever this information is available * @@ -77,7 +77,7 @@ interface ResultObserver { * Receive the each record present on the {@link @Result} * @param {Record} record The {@link Record} produced */ - onNext?: (record: Record) => void + onNext?: (record: Record) => void /** * Called when the result is fully received @@ -111,7 +111,7 @@ interface QueuedResultObserver extends ResultObserver { * Alternatively can be consumed lazily using {@link Result#subscribe} function. * @access public */ -class Result implements Promise> { +class Result implements Promise> { private readonly _stack: string | null private readonly _streamObserverPromise: Promise private _p: Promise | null @@ -212,7 +212,7 @@ class Result implements Promise> { + private _getOrCreatePromise (): Promise> { if (this._p == null) { this._p = new Promise((resolve, reject) => { const records: Record[] = [] @@ -240,9 +240,9 @@ class Result implements Promise, ResultSummary>} The async iterator for the Results + * @returns {PeekableAsyncIterator, ResultSummary>} The async iterator for the Results */ - [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { + [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { if (!this.isOpen()) { const error = newError('Result is already consumed') return { @@ -345,9 +345,9 @@ class Result implements Promise, TResult2 = never>( + then, TResult2 = never>( onFulfilled?: - | ((value: QueryResult) => TResult1 | PromiseLike) + | ((value: QueryResult) => TResult1 | PromiseLike) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike) | null ): Promise { @@ -364,7 +364,7 @@ class Result implements Promise( onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise | TResult> { + ): Promise | TResult> { return this._getOrCreatePromise().catch(onRejected) } @@ -376,7 +376,7 @@ class Result implements Promise void) | null): Promise> { + finally (onfinally?: (() => void) | null): Promise> { return this._getOrCreatePromise().finally(onfinally) } @@ -391,7 +391,7 @@ class Result implements Promise): void { + subscribe (observer: ResultObserver): void { this._subscribe(observer) .catch(() => {}) } diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 73cd24e81..09c5a1892 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -155,11 +155,11 @@ class Session { * @param {TransactionConfig} [transactionConfig] - Configuration for the new auto-commit transaction. * @return {Result} New Result. */ - run ( + run ( query: Query, parameters?: any, transactionConfig?: TransactionConfig - ): Result { + ): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/core/src/transaction-managed.ts b/packages/core/src/transaction-managed.ts index 6efbcc704..e40206598 100644 --- a/packages/core/src/transaction-managed.ts +++ b/packages/core/src/transaction-managed.ts @@ -61,7 +61,7 @@ class ManagedTransaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { return this._run(query, parameters) } } diff --git a/packages/core/src/transaction.ts b/packages/core/src/transaction.ts index 054df751d..3bee6d480 100644 --- a/packages/core/src/transaction.ts +++ b/packages/core/src/transaction.ts @@ -175,7 +175,7 @@ class Transaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/neo4j-driver-deno/lib/core/result.ts b/packages/neo4j-driver-deno/lib/core/result.ts index 66a1f19e4..ab3e8a4df 100644 --- a/packages/neo4j-driver-deno/lib/core/result.ts +++ b/packages/neo4j-driver-deno/lib/core/result.ts @@ -56,8 +56,8 @@ const DEFAULT_ON_KEYS = (keys: string[]): void => {} * The query result is the combination of the {@link ResultSummary} and * the array {@link Record[]} produced by the query */ -interface QueryResult { - records: Array> +interface QueryResult { + records: Array> summary: ResultSummary } @@ -65,7 +65,7 @@ interface QueryResult { * Interface to observe updates on the Result which is being produced. * */ -interface ResultObserver { +interface ResultObserver { /** * Receive the keys present on the record whenever this information is available * @@ -77,7 +77,7 @@ interface ResultObserver { * Receive the each record present on the {@link @Result} * @param {Record} record The {@link Record} produced */ - onNext?: (record: Record) => void + onNext?: (record: Record) => void /** * Called when the result is fully received @@ -111,7 +111,7 @@ interface QueuedResultObserver extends ResultObserver { * Alternatively can be consumed lazily using {@link Result#subscribe} function. * @access public */ -class Result implements Promise> { +class Result implements Promise> { private readonly _stack: string | null private readonly _streamObserverPromise: Promise private _p: Promise | null @@ -212,7 +212,7 @@ class Result implements Promise> { + private _getOrCreatePromise (): Promise> { if (this._p == null) { this._p = new Promise((resolve, reject) => { const records: Record[] = [] @@ -240,9 +240,9 @@ class Result implements Promise, ResultSummary>} The async iterator for the Results + * @returns {PeekableAsyncIterator, ResultSummary>} The async iterator for the Results */ - [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { + [Symbol.asyncIterator] (): PeekableAsyncIterator, ResultSummary> { if (!this.isOpen()) { const error = newError('Result is already consumed') return { @@ -345,9 +345,9 @@ class Result implements Promise, TResult2 = never>( + then, TResult2 = never>( onFulfilled?: - | ((value: QueryResult) => TResult1 | PromiseLike) + | ((value: QueryResult) => TResult1 | PromiseLike) | null, onRejected?: ((reason: any) => TResult2 | PromiseLike) | null ): Promise { @@ -364,7 +364,7 @@ class Result implements Promise( onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise | TResult> { + ): Promise | TResult> { return this._getOrCreatePromise().catch(onRejected) } @@ -376,7 +376,7 @@ class Result implements Promise void) | null): Promise> { + finally (onfinally?: (() => void) | null): Promise> { return this._getOrCreatePromise().finally(onfinally) } @@ -391,7 +391,7 @@ class Result implements Promise): void { + subscribe (observer: ResultObserver): void { this._subscribe(observer) .catch(() => {}) } diff --git a/packages/neo4j-driver-deno/lib/core/session.ts b/packages/neo4j-driver-deno/lib/core/session.ts index 952089802..fd6770345 100644 --- a/packages/neo4j-driver-deno/lib/core/session.ts +++ b/packages/neo4j-driver-deno/lib/core/session.ts @@ -155,11 +155,11 @@ class Session { * @param {TransactionConfig} [transactionConfig] - Configuration for the new auto-commit transaction. * @return {Result} New Result. */ - run ( + run ( query: Query, parameters?: any, transactionConfig?: TransactionConfig - ): Result { + ): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters diff --git a/packages/neo4j-driver-deno/lib/core/transaction-managed.ts b/packages/neo4j-driver-deno/lib/core/transaction-managed.ts index f0541aaa2..b3283a2ff 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction-managed.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction-managed.ts @@ -61,7 +61,7 @@ class ManagedTransaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { return this._run(query, parameters) } } diff --git a/packages/neo4j-driver-deno/lib/core/transaction.ts b/packages/neo4j-driver-deno/lib/core/transaction.ts index 31af071db..5e4b98b96 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction.ts @@ -175,7 +175,7 @@ class Transaction { * @param {Object} parameters - Map with parameters to use in query * @return {Result} New Result */ - run (query: Query, parameters?: any): Result { + run (query: Query, parameters?: any): Result { const { validatedQuery, params } = validateQueryAndParameters( query, parameters