From 3d9d59a463bc02b9bd0a43de43ca943f4f34c130 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 19 Oct 2022 17:28:16 +0200 Subject: [PATCH 01/27] Database Notification Improvements --- packages/core/src/index.ts | 18 ++- packages/core/src/result-summary.ts | 149 +++++++++++++++++- packages/neo4j-driver-deno/lib/core/index.ts | 18 ++- .../lib/core/result-summary.ts | 149 +++++++++++++++++- packages/neo4j-driver-deno/lib/mod.ts | 18 ++- packages/neo4j-driver-lite/src/index.ts | 18 ++- .../neo4j-driver-lite/test/unit/index.test.ts | 18 +++ packages/neo4j-driver/src/index.js | 12 +- .../neo4j-driver/test/types/index.test.ts | 28 +++- packages/neo4j-driver/types/index.d.ts | 16 +- 10 files changed, 417 insertions(+), 27 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 8f0bb0ff1..4a7d893d8 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -64,7 +64,11 @@ import ResultSummary, { Plan, ProfiledPlan, QueryStatistics, - Stats + Stats, + NotificationSeverityLevel, + NotificationCategory, + notificationCategory, + notificationSeverityLevel } from './result-summary' import Result, { QueryResult, ResultObserver } from './result' import ConnectionProvider from './connection-provider' @@ -149,7 +153,9 @@ const forExport = { driver, json, auth, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export { @@ -209,7 +215,9 @@ export { driver, json, auth, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export type { @@ -221,7 +229,9 @@ export type { TransactionConfig, BookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + NotificationCategory, + NotificationSeverityLevel } export default forExport diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index 1431fefa2..3bbaadf8e 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -419,6 +419,43 @@ interface NotificationPosition { column?: number } +type NotificationSeverityLevel = 'WARNING' | 'INFORMATION' | 'UNKNOWN' +/** + * @typedef {'WARNING' | 'INFORMATION' | 'UNKNOWN'} NotificationSeverityLevel + */ +/** + * Constants that represents the Severity level in the {@link Notification} + */ +const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { + WARNING: 'WARNING', + INFORMATION: 'INFORMATION', + UNKNOWN: 'UNKNOWN' +} + +Object.freeze(notificationSeverityLevel) +const severityLevels = Object.values(notificationSeverityLevel) + +type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | +'DEPRECATION' | 'RUNTIME' | 'UNKNOWN' +/** + * @typedef {'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + */ +/** + * Constants that represents the Category in the {@link Notification} + */ +const notificationCategory: { [key in NotificationCategory]: key } = { + HINT: 'HINT', + QUERY: 'QUERY', + UNSUPPORTED: 'UNSUPPORTED', + PERFORMANCE: 'PERFORMANCE', + DEPRECATION: 'DEPRECATION', + RUNTIME: 'RUNTIME', + UNKNOWN: 'UNKNOWN' +} + +Object.freeze(notificationCategory) +const categories = Object.values(notificationCategory) + /** * Class for Cypher notifications * @access public @@ -429,6 +466,10 @@ class Notification { description: string severity: string position: NotificationPosition | {} + severityLevel: NotificationSeverityLevel + category: NotificationCategory + rawSeverityLevel: string + rawCategory: string /** * Create a Notification instance @@ -436,11 +477,111 @@ class Notification { * @param {Object} notification - Object with notification data */ constructor (notification: any) { + /** + * The code + * @type {string} + * @public + */ this.code = notification.code + /** + * The title + * @type {string} + * @public + */ this.title = notification.title + /** + * The description + * @type {string} + * @public + */ this.description = notification.description + /** + * The raw severity + * @type {string} + * @public + * @deprecated This property will be removed in 6.0. + * Use {@link Notification.rawSeverityLevel} for the raw value or {@link Notification.severityLevel} enumerated value. + */ this.severity = notification.severity + /** + * The position which the notification had occur. + * + * @type {NotificationPosition} + * @public + */ this.position = Notification._constructPosition(notification.position) + + /** + * The severity level + * + * @type {NotificationSeverityLevel} + * @public + * @example + * const { summary } = await session.run("RETURN 1") + * + * for (const notification of summary.notifications) { + * switch(notification.severityLevel) { + * case neo4j.notificationSeverityLevel.INFORMATION: // or simply 'INFORMATION' + * console.info(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationSeverityLevel.WARNING: // or simply 'WARNING' + * console.warn(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationSeverityLevel.UNKNOWN: // or simply 'UNKNOWN' + * default: + * // the raw info came from the server could be found at notification.rawSeverityLevel + * console.log(`${notification.title} - ${notification.description}`) + * break + * } + * } + */ + this.severityLevel = severityLevels.includes(notification.severity) + ? notification.severity + : notificationSeverityLevel.UNKNOWN + + /** + * The severity level returned by the server without any validation. + * + * @type {string} + * @public + */ + this.rawSeverityLevel = notification.severity + + /** + * The severity level + * + * @type {NotificationCategory} + * @public + * @example + * const { summary } = await session.run("RETURN 1") + * + * for (const notification of summary.notifications) { + * switch(notification.category) { + * case neo4j.notificationCategory.QUERY: // or simply 'QUERY' + * console.info(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationCategory.PERFORMANCE: // or simply 'PERFORMANCE' + * console.warn(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationCategory.UNKNOWN: // or simply 'UNKNOWN' + * default: + * // the raw info came from the server could be found at notification.rawCategory + * console.log(`${notification.title} - ${notification.description}`) + * break + * } + * } + */ + this.category = categories.includes(notification.category) + ? notification.category + : notificationCategory.UNKNOWN + + /** + * The category returned by the server without any validation. + * + * @type {string} + * @public + */ + this.rawCategory = notification.category } static _constructPosition (pos: NotificationPosition): NotificationPosition { @@ -540,10 +681,14 @@ export { Plan, ProfiledPlan, QueryStatistics, - Stats + Stats, + notificationSeverityLevel, + notificationCategory } export type { - NotificationPosition + NotificationPosition, + NotificationSeverityLevel, + NotificationCategory } export default ResultSummary diff --git a/packages/neo4j-driver-deno/lib/core/index.ts b/packages/neo4j-driver-deno/lib/core/index.ts index 1cd30eeea..cd8be6ccb 100644 --- a/packages/neo4j-driver-deno/lib/core/index.ts +++ b/packages/neo4j-driver-deno/lib/core/index.ts @@ -64,7 +64,11 @@ import ResultSummary, { Plan, ProfiledPlan, QueryStatistics, - Stats + Stats, + NotificationSeverityLevel, + NotificationCategory, + notificationCategory, + notificationSeverityLevel } from './result-summary.ts' import Result, { QueryResult, ResultObserver } from './result.ts' import ConnectionProvider from './connection-provider.ts' @@ -149,7 +153,9 @@ const forExport = { driver, json, auth, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export { @@ -209,7 +215,9 @@ export { driver, json, auth, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export type { @@ -221,7 +229,9 @@ export type { TransactionConfig, BookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + NotificationCategory, + NotificationSeverityLevel } export default forExport diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index 771075922..4e362f2ff 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -419,6 +419,43 @@ interface NotificationPosition { column?: number } +type NotificationSeverityLevel = 'WARNING' | 'INFORMATION' | 'UNKNOWN' +/** + * @typedef {'WARNING' | 'INFORMATION' | 'UNKNOWN'} NotificationSeverityLevel + */ +/** + * Constants that represents the Severity level in the {@link Notification} + */ +const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { + WARNING: 'WARNING', + INFORMATION: 'INFORMATION', + UNKNOWN: 'UNKNOWN' +} + +Object.freeze(notificationSeverityLevel) +const severityLevels = Object.values(notificationSeverityLevel) + +type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | +'DEPRECATION' | 'RUNTIME' | 'UNKNOWN' +/** + * @typedef {'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + */ +/** + * Constants that represents the Category in the {@link Notification} + */ +const notificationCategory: { [key in NotificationCategory]: key } = { + HINT: 'HINT', + QUERY: 'QUERY', + UNSUPPORTED: 'UNSUPPORTED', + PERFORMANCE: 'PERFORMANCE', + DEPRECATION: 'DEPRECATION', + RUNTIME: 'RUNTIME', + UNKNOWN: 'UNKNOWN' +} + +Object.freeze(notificationCategory) +const categories = Object.values(notificationCategory) + /** * Class for Cypher notifications * @access public @@ -429,6 +466,10 @@ class Notification { description: string severity: string position: NotificationPosition | {} + severityLevel: NotificationSeverityLevel + category: NotificationCategory + rawSeverityLevel: string + rawCategory: string /** * Create a Notification instance @@ -436,11 +477,111 @@ class Notification { * @param {Object} notification - Object with notification data */ constructor (notification: any) { + /** + * The code + * @type {string} + * @public + */ this.code = notification.code + /** + * The title + * @type {string} + * @public + */ this.title = notification.title + /** + * The description + * @type {string} + * @public + */ this.description = notification.description + /** + * The raw severity + * @type {string} + * @public + * @deprecated This property will be removed in 6.0. + * Use {@link Notification.rawSeverityLevel} for the raw value or {@link Notification.severityLevel} enumerated value. + */ this.severity = notification.severity + /** + * The position which the notification had occur. + * + * @type {NotificationPosition} + * @public + */ this.position = Notification._constructPosition(notification.position) + + /** + * The severity level + * + * @type {NotificationSeverityLevel} + * @public + * @example + * const { summary } = await session.run("RETURN 1") + * + * for (const notification of summary.notifications) { + * switch(notification.severityLevel) { + * case neo4j.notificationSeverityLevel.INFORMATION: // or simply 'INFORMATION' + * console.info(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationSeverityLevel.WARNING: // or simply 'WARNING' + * console.warn(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationSeverityLevel.UNKNOWN: // or simply 'UNKNOWN' + * default: + * // the raw info came from the server could be found at notification.rawSeverityLevel + * console.log(`${notification.title} - ${notification.description}`) + * break + * } + * } + */ + this.severityLevel = severityLevels.includes(notification.severity) + ? notification.severity + : notificationSeverityLevel.UNKNOWN + + /** + * The severity level returned by the server without any validation. + * + * @type {string} + * @public + */ + this.rawSeverityLevel = notification.severity + + /** + * The severity level + * + * @type {NotificationCategory} + * @public + * @example + * const { summary } = await session.run("RETURN 1") + * + * for (const notification of summary.notifications) { + * switch(notification.category) { + * case neo4j.notificationCategory.QUERY: // or simply 'QUERY' + * console.info(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationCategory.PERFORMANCE: // or simply 'PERFORMANCE' + * console.warn(`${notification.title} - ${notification.description}`) + * break + * case neo4j.notificationCategory.UNKNOWN: // or simply 'UNKNOWN' + * default: + * // the raw info came from the server could be found at notification.rawCategory + * console.log(`${notification.title} - ${notification.description}`) + * break + * } + * } + */ + this.category = categories.includes(notification.category) + ? notification.category + : notificationCategory.UNKNOWN + + /** + * The category returned by the server without any validation. + * + * @type {string} + * @public + */ + this.rawCategory = notification.category } static _constructPosition (pos: NotificationPosition): NotificationPosition { @@ -540,10 +681,14 @@ export { Plan, ProfiledPlan, QueryStatistics, - Stats + Stats, + notificationSeverityLevel, + notificationCategory } export type { - NotificationPosition + NotificationPosition, + NotificationSeverityLevel, + NotificationCategory } export default ResultSummary diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 0c221215b..29fae91d5 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -73,7 +73,11 @@ import { BookmarkManager, bookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + notificationCategory, + notificationSeverityLevel, + NotificationSeverityLevel, + NotificationCategory } from './core/index.ts' // @deno-types=./bolt-connection/types/index.d.ts import { @@ -466,7 +470,9 @@ const forExport = { DateTime, ConnectionProvider, Connection, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export { @@ -519,7 +525,9 @@ export { DateTime, ConnectionProvider, Connection, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export type { QueryResult, @@ -532,6 +540,8 @@ export type { NotificationPosition, BookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + NotificationCategory, + NotificationSeverityLevel } export default forExport diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index ed5abc899..143f71a19 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -73,7 +73,11 @@ import { BookmarkManager, bookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + notificationCategory, + notificationSeverityLevel, + NotificationSeverityLevel, + NotificationCategory } from 'neo4j-driver-core' import { DirectConnectionProvider, @@ -465,7 +469,9 @@ const forExport = { DateTime, ConnectionProvider, Connection, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export { @@ -518,7 +524,9 @@ export { DateTime, ConnectionProvider, Connection, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export type { QueryResult, @@ -531,6 +539,8 @@ export type { NotificationPosition, BookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + NotificationCategory, + NotificationSeverityLevel } export default forExport diff --git a/packages/neo4j-driver-lite/test/unit/index.test.ts b/packages/neo4j-driver-lite/test/unit/index.test.ts index c19f37ae6..b7e9945c1 100644 --- a/packages/neo4j-driver-lite/test/unit/index.test.ts +++ b/packages/neo4j-driver-lite/test/unit/index.test.ts @@ -307,4 +307,22 @@ describe('index', () => { const date: DateTime = new DateTime(1, 2, 3, 3, 5, 6, 6, 5) expect(date).toBeDefined() }) + + it('should export notificationSeverityLevel', () => { + expect(neo4j.notificationSeverityLevel).toBeDefined() + expect(neo4j.notificationSeverityLevel.WARNING).toBeDefined() + expect(neo4j.notificationSeverityLevel.INFORMATION).toBeDefined() + expect(neo4j.notificationSeverityLevel.UNKNOWN).toBeDefined() + }) + + it('should export notificationCategory', () => { + expect(neo4j.notificationCategory).toBeDefined() + expect(neo4j.notificationCategory.HINT).toBeDefined() + expect(neo4j.notificationCategory.QUERY).toBeDefined() + expect(neo4j.notificationCategory.UNSUPPORTED).toBeDefined() + expect(neo4j.notificationCategory.PERFORMANCE).toBeDefined() + expect(neo4j.notificationCategory.DEPRECATION).toBeDefined() + expect(neo4j.notificationCategory.RUNTIME).toBeDefined() + expect(neo4j.notificationCategory.UNKNOWN).toBeDefined() + }) }) diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 101e5ab6e..264c830a6 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -61,7 +61,9 @@ import { Session, Transaction, ManagedTransaction, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } from 'neo4j-driver-core' import { DirectConnectionProvider, @@ -455,7 +457,9 @@ const forExport = { Date, LocalDateTime, DateTime, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export { @@ -509,6 +513,8 @@ export { Date, LocalDateTime, DateTime, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export default forExport diff --git a/packages/neo4j-driver/test/types/index.test.ts b/packages/neo4j-driver/test/types/index.test.ts index 97dbbde01..cb6cb734b 100644 --- a/packages/neo4j-driver/test/types/index.test.ts +++ b/packages/neo4j-driver/test/types/index.test.ts @@ -28,7 +28,11 @@ import { session, spatial, temporal, - DateTime + DateTime, + notificationSeverityLevel, + NotificationSeverityLevel, + notificationCategory, + NotificationCategory } from '../../types/index' import Driver from '../../types/driver' @@ -86,3 +90,25 @@ const isNeo4jLocalDateTime: boolean = temporal.isLocalDateTime({}) const isNeo4jLocalTime: boolean = temporal.isLocalTime({}) const isNeo4jTime: boolean = temporal.isTime({}) const dateTime = DateTime.fromStandardDate(new Date()) + +const unknownSeverityString: string = notificationSeverityLevel.UNKNOWN +const warningSeverityString: string = notificationSeverityLevel.WARNING +const informationSeverityString: string = notificationSeverityLevel.INFORMATION +const unknownSeverity: NotificationSeverityLevel = notificationSeverityLevel.UNKNOWN +const warningSeverity: NotificationSeverityLevel = notificationSeverityLevel.WARNING +const informationSeverity: NotificationSeverityLevel = notificationSeverityLevel.INFORMATION + +const hintCategoryString: string = notificationCategory.HINT +const deprecationCategoryString: string = notificationCategory.DEPRECATION +const performanceCategoryString: string = notificationCategory.PERFORMANCE +const queryCategoryString: string = notificationCategory.QUERY +const runtimeCategoryString: string = notificationCategory.RUNTIME +const unsupportedCategoryString: string = notificationCategory.UNSUPPORTED +const unknownCategoryString: string = notificationCategory.UNKNOWN +const hintCategory: NotificationCategory = notificationCategory.HINT +const deprecationCategory: NotificationCategory = notificationCategory.DEPRECATION +const performanceCategory: NotificationCategory = notificationCategory.PERFORMANCE +const queryCategory: NotificationCategory = notificationCategory.QUERY +const runtimeCategory: NotificationCategory = notificationCategory.RUNTIME +const unsupportedCategory: NotificationCategory = notificationCategory.UNSUPPORTED +const unknownCategory: NotificationCategory = notificationCategory.UNKNOWN diff --git a/packages/neo4j-driver/types/index.d.ts b/packages/neo4j-driver/types/index.d.ts index 16a570dfd..e0b256735 100644 --- a/packages/neo4j-driver/types/index.d.ts +++ b/packages/neo4j-driver/types/index.d.ts @@ -64,7 +64,11 @@ import { BookmarkManager, bookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + notificationCategory, + notificationSeverityLevel, + NotificationCategory, + NotificationSeverityLevel } from 'neo4j-driver-core' import { AuthToken, @@ -225,6 +229,8 @@ declare const forExport: { isLocalDateTime: typeof isLocalDateTime isDateTime: typeof isDateTime bookmarkManager: typeof bookmarkManager + notificationCategory: typeof notificationCategory + notificationSeverityLevel: typeof notificationSeverityLevel } export { @@ -286,13 +292,17 @@ export { isDate, isLocalDateTime, isDateTime, - bookmarkManager + bookmarkManager, + notificationCategory, + notificationSeverityLevel } export type { BookmarkManager, BookmarkManagerConfig, - SessionConfig + SessionConfig, + NotificationCategory, + NotificationSeverityLevel } export default forExport From d1cac6ee6b8482ba2d07e81ed239f25588ccfbc1 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 19 Oct 2022 18:17:13 +0200 Subject: [PATCH 02/27] Add tests for Notification, notificationSeverityLevel and notificationCategory --- packages/core/src/result-summary.ts | 4 +- packages/core/test/result-summary.test.ts | 131 +++++++++++++++++- .../lib/core/result-summary.ts | 4 +- 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index 3bbaadf8e..ebbf8fff9 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -469,7 +469,7 @@ class Notification { severityLevel: NotificationSeverityLevel category: NotificationCategory rawSeverityLevel: string - rawCategory: string + rawCategory?: string /** * Create a Notification instance @@ -578,7 +578,7 @@ class Notification { /** * The category returned by the server without any validation. * - * @type {string} + * @type {string|undefined} * @public */ this.rawCategory = notification.category diff --git a/packages/core/test/result-summary.test.ts b/packages/core/test/result-summary.test.ts index cc3ea7c44..7aca88e14 100644 --- a/packages/core/test/result-summary.test.ts +++ b/packages/core/test/result-summary.test.ts @@ -17,7 +17,14 @@ * limitations under the License. */ -import { ServerInfo } from '../src/result-summary' +import { + ServerInfo, + Notification, + NotificationSeverityLevel, + NotificationCategory, + notificationSeverityLevel, + notificationCategory +} from '../src/result-summary' describe('ServerInfo', () => { it.each([ @@ -48,3 +55,125 @@ describe('ServerInfo', () => { } ) }) + +describe('Notification', () => { + describe('.severityLevel', () => { + it.each(getValidSeverityLevels())('should fill severityLevel with the rawSeverityLevel equals to %s', rawSeverityLevel => { + const rawNotification = { + severity: rawSeverityLevel + } + + const notification = new Notification(rawNotification) + + expect(notification.severityLevel).toBe(rawSeverityLevel) + expect(notification.rawSeverityLevel).toBe(rawSeverityLevel) + }) + + it.each([ + 'UNKNOWN', + null, + undefined, + 'I_AM_NOT_OKAY', + 'information' + ])('should fill severityLevel UNKNOWN if the rawSeverityLevel equals to %s', rawSeverityLevel => { + const rawNotification = { + severity: rawSeverityLevel + } + + const notification = new Notification(rawNotification) + + expect(notification.severityLevel).toBe('UNKNOWN') + expect(notification.rawSeverityLevel).toBe(rawSeverityLevel) + }) + }) + + describe('.category', () => { + it.each(getValidCategories())('should fill category with the rawCategory equals to %s', rawCategory => { + const rawNotification = { + category: rawCategory + } + + const notification = new Notification(rawNotification) + + expect(notification.category).toBe(rawCategory) + expect(notification.rawCategory).toBe(rawCategory) + }) + + it.each([ + 'UNKNOWN', + undefined, + null, + 'DUNNO', + 'deprecation' + ])('should fill category with UNKNOWN the rawCategory equals to %s', rawCategory => { + const rawNotification = { + category: rawCategory + } + + const notification = new Notification(rawNotification) + + expect(notification.category).toBe('UNKNOWN') + expect(notification.rawCategory).toBe(rawCategory) + }) + }) +}) + +describe('notificationSeverityLevel', () => { + it('should have keys equals to values', () => { + for (const [key, value] of Object.entries(notificationSeverityLevel)) { + expect(key).toEqual(value) + } + }) + + it('should values be assignable to NotificationSeverityLevel', () => { + for (const [, value] of Object.entries(notificationSeverityLevel)) { + const assignableValue: NotificationSeverityLevel = value + expect(assignableValue).toBeDefined() + } + }) + + it.each(getValidSeverityLevels())('should have %s as key', (severity) => { + const keys = Object.keys(notificationSeverityLevel) + expect(keys.includes(severity)).toBe(true) + }) +}) + +describe('notificationCategory', () => { + it('should have keys equals to values', () => { + for (const [key, value] of Object.entries(notificationCategory)) { + expect(key).toEqual(value) + } + }) + + it('should values be assignable to NotificationCategory', () => { + for (const [, value] of Object.entries(notificationCategory)) { + const assignableValue: NotificationCategory = value + expect(assignableValue).toBeDefined() + } + }) + + it.each(getValidCategories())('should have %s as key', (category) => { + const keys = Object.keys(notificationCategory) + expect(keys.includes(category)).toBe(true) + }) +}) + +function getValidSeverityLevels (): NotificationSeverityLevel[] { + return [ + 'WARNING', + 'INFORMATION', + 'UNKNOWN' + ] +} + +function getValidCategories (): NotificationCategory[] { + return [ + 'HINT', + 'QUERY', + 'UNSUPPORTED', + 'PERFORMANCE', + 'DEPRECATION', + 'RUNTIME', + 'UNKNOWN' + ] +} diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index 4e362f2ff..c0e2a8557 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -469,7 +469,7 @@ class Notification { severityLevel: NotificationSeverityLevel category: NotificationCategory rawSeverityLevel: string - rawCategory: string + rawCategory?: string /** * Create a Notification instance @@ -578,7 +578,7 @@ class Notification { /** * The category returned by the server without any validation. * - * @type {string} + * @type {string|undefined} * @public */ this.rawCategory = notification.category From eec82a818d72e379f19fc44d706a7acdc7745857 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 20 Oct 2022 18:30:06 +0200 Subject: [PATCH 03/27] Assert not sending notification filters the current implementations of bolt --- .../src/bolt/bolt-protocol-util.js | 23 +++++++++++++++++-- .../src/bolt/bolt-protocol-v1.js | 9 ++++++-- .../src/bolt/bolt-protocol-v3.js | 7 ++++-- .../src/bolt/bolt-protocol-v4x1.js | 6 ++++- .../src/bolt/bolt-protocol-v4x3.js | 7 +++++- .../src/bolt/bolt-protocol-v5x0.js | 7 +++++- .../bolt/bolt-protocol-util.js | 23 +++++++++++++++++-- .../bolt-connection/bolt/bolt-protocol-v1.js | 9 ++++++-- .../bolt-connection/bolt/bolt-protocol-v3.js | 7 ++++-- .../bolt/bolt-protocol-v4x1.js | 6 ++++- .../bolt/bolt-protocol-v4x3.js | 7 +++++- .../bolt/bolt-protocol-v5x0.js | 7 +++++- 12 files changed, 100 insertions(+), 18 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-util.js b/packages/bolt-connection/src/bolt/bolt-protocol-util.js index 8a6ef2e4d..7993435d1 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-util.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-util.js @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { newError } from 'neo4j-driver-core' +import { newError, json } from 'neo4j-driver-core' // eslint-disable-next-line no-unused-vars import { ResultStreamObserver } from './stream-observers' @@ -79,4 +79,23 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = } } -export { assertDatabaseIsEmpty, assertTxConfigIsEmpty, assertImpersonatedUserIsEmpty } +/* Asserts that the passed-in notificationFilters is empty + * @param {string[]} notificationFilters + * @param {function (err:Error)} onProtocolError Called when it does have notificationFilters user set + * @param {any} observer + */ +function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { + if (notificationFilters != null) { + const error = newError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${json.stringify(notificationFilters)}.` + ) + // unsupported API was used, consider this a fatal error for the current connection + onProtocolError(error.message) + observer.onError(error) + throw error + } +} + +export { assertDatabaseIsEmpty, assertTxConfigIsEmpty, assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index f3a7b32a0..4e6a5820f 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -19,7 +19,8 @@ import { assertDatabaseIsEmpty, assertTxConfigIsEmpty, - assertImpersonatedUserIsEmpty + assertImpersonatedUserIsEmpty, + assertNotificationFiltersIsEmpty } from './bolt-protocol-util' // eslint-disable-next-line no-unused-vars import { Chunker } from '../channel' @@ -149,14 +150,18 @@ export default class BoltProtocol { * @param {Object} param.authToken the authentication token. * @param {function(err: Error)} param.onError the callback to invoke on error. * @param {function()} param.onComplete the callback to invoke on completion. + * @param {?string[]} param.notificationFilters the filtering for notifications. * @returns {StreamObserver} the stream observer that monitors the corresponding server response. */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + 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 25dd7ad5a..503298384 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -18,7 +18,7 @@ */ import BoltProtocolV2 from './bolt-protocol-v2' import RequestMessage from './request-message' -import { assertDatabaseIsEmpty, assertImpersonatedUserIsEmpty } from './bolt-protocol-util' +import { assertDatabaseIsEmpty, assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } from './bolt-protocol-util' import { StreamObserver, LoginObserver, @@ -69,12 +69,15 @@ export default class BoltProtocol extends BoltProtocolV2 { return metadata } - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + 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 f8a1de208..ee31b99e9 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x1.js @@ -23,6 +23,7 @@ import { internal } from 'neo4j-driver-core' import transformersFactories from './bolt-protocol-v4x1.transformer' import Transformer from './transformer' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util' const { constants: { BOLT_PROTOCOL_V4_1 } @@ -72,12 +73,15 @@ export default class BoltProtocol extends BoltProtocolV4 { return this._transformer } - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js index 3ca55e4f4..823a27401 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x3.js @@ -25,6 +25,7 @@ import utcTransformersFactories from './bolt-protocol-v5x0.utc.transformer' import Transformer from './transformer' import { internal } from 'neo4j-driver-core' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util' const { bookmarks: { Bookmarks }, @@ -85,9 +86,10 @@ export default class BoltProtocol extends BoltProtocolV42 { * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => { @@ -98,6 +100,9 @@ export default class BoltProtocol extends BoltProtocolV42 { } }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), observer, diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js index 8cfbefa2b..09db2d255 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x0.js @@ -24,6 +24,7 @@ import RequestMessage from './request-message' import { LoginObserver } from './stream-observers' import { internal } from 'neo4j-driver-core' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util' const { constants: { BOLT_PROTOCOL_V5_0 } @@ -49,14 +50,18 @@ export default class BoltProtocol extends BoltProtocolV44 { * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js index 21293dd7a..c85fc8bdd 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { newError } from '../../core/index.ts' +import { newError, json } from '../../core/index.ts' // eslint-disable-next-line no-unused-vars import { ResultStreamObserver } from './stream-observers.js' @@ -79,4 +79,23 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = } } -export { assertDatabaseIsEmpty, assertTxConfigIsEmpty, assertImpersonatedUserIsEmpty } +/* Asserts that the passed-in notificationFilters is empty + * @param {string[]} notificationFilters + * @param {function (err:Error)} onProtocolError Called when it does have notificationFilters user set + * @param {any} observer + */ +function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { + if (notificationFilters != null) { + const error = newError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${json.stringify(notificationFilters)}.` + ) + // unsupported API was used, consider this a fatal error for the current connection + onProtocolError(error.message) + observer.onError(error) + throw error + } +} + +export { assertDatabaseIsEmpty, assertTxConfigIsEmpty, assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } 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 20bf7a31b..2bf061402 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 @@ -19,7 +19,8 @@ import { assertDatabaseIsEmpty, assertTxConfigIsEmpty, - assertImpersonatedUserIsEmpty + assertImpersonatedUserIsEmpty, + assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' // eslint-disable-next-line no-unused-vars import { Chunker } from '../channel/index.js' @@ -149,14 +150,18 @@ export default class BoltProtocol { * @param {Object} param.authToken the authentication token. * @param {function(err: Error)} param.onError the callback to invoke on error. * @param {function()} param.onComplete the callback to invoke on completion. + * @param {?string[]} param.notificationFilters the filtering for notifications. * @returns {StreamObserver} the stream observer that monitors the corresponding server response. */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + 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 57b5632ae..20d11019d 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 @@ -18,7 +18,7 @@ */ import BoltProtocolV2 from './bolt-protocol-v2.js' import RequestMessage from './request-message.js' -import { assertDatabaseIsEmpty, assertImpersonatedUserIsEmpty } from './bolt-protocol-util.js' +import { assertDatabaseIsEmpty, assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' import { StreamObserver, LoginObserver, @@ -69,12 +69,15 @@ export default class BoltProtocol extends BoltProtocolV2 { return metadata } - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + 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 001f41e68..b8ebdf333 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 @@ -23,6 +23,7 @@ import { internal } from '../../core/index.ts' import transformersFactories from './bolt-protocol-v4x1.transformer.js' import Transformer from './transformer.js' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' const { constants: { BOLT_PROTOCOL_V4_1 } @@ -72,12 +73,15 @@ export default class BoltProtocol extends BoltProtocolV4 { return this._transformer } - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, 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 86d62a1f1..e6e3626fe 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 @@ -25,6 +25,7 @@ import utcTransformersFactories from './bolt-protocol-v5x0.utc.transformer.js' import Transformer from './transformer.js' import { internal } from '../../core/index.ts' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' const { bookmarks: { Bookmarks }, @@ -85,9 +86,10 @@ export default class BoltProtocol extends BoltProtocolV42 { * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => { @@ -98,6 +100,9 @@ export default class BoltProtocol extends BoltProtocolV42 { } }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting, ['utc']), observer, 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 4e30ccb0b..07cd95e7d 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 @@ -24,6 +24,7 @@ import RequestMessage from './request-message.js' import { LoginObserver } from './stream-observers.js' import { internal } from '../../core/index.ts' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' const { constants: { BOLT_PROTOCOL_V5_0 } @@ -49,14 +50,18 @@ export default class BoltProtocol extends BoltProtocolV44 { * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ - initialize ({ userAgent, authToken, onError, onComplete } = {}) { + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { const observer = new LoginObserver({ onError: error => this._onLoginError(error, onError), onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.hello(userAgent, authToken, this._serversideRouting), observer, From bac205a02ab4c0743b7f9af1907b9b8d96d2c335 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 26 Oct 2022 17:34:52 +0200 Subject: [PATCH 04/27] Implement notification filters validation from v1 to v5.0 --- .../src/bolt/bolt-protocol-v1.js | 7 +++ .../src/bolt/bolt-protocol-v3.js | 6 +++ .../src/bolt/bolt-protocol-v4x0.js | 8 ++- .../src/bolt/bolt-protocol-v4x4.js | 10 ++++ .../test/bolt/bolt-protocol-v1.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v2.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v3.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v4x0.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v4x1.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v4x2.test.js | 54 +++++++++++++++++++ .../test/bolt/bolt-protocol-v4x3.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v4x4.test.js | 53 ++++++++++++++++++ .../test/bolt/bolt-protocol-v5x0.test.js | 53 ++++++++++++++++++ .../bolt-connection/bolt/bolt-protocol-v1.js | 7 +++ .../bolt-connection/bolt/bolt-protocol-v3.js | 6 +++ .../bolt/bolt-protocol-v4x0.js | 8 ++- .../bolt/bolt-protocol-v4x4.js | 10 ++++ 17 files changed, 538 insertions(+), 2 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js index 4e6a5820f..c3a1f2381 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v1.js @@ -182,6 +182,7 @@ export default class BoltProtocol { * @param {string} param.database the target database name. * @param {string} param.mode the access mode. * @param {string} param.impersonatedUser the impersonated user + * @param {?string[]} param.notificationFilters the filtering for notifications. * @param {function(err: Error)} param.beforeError the callback to invoke before handling the error. * @param {function(err: Error)} param.afterError the callback to invoke after handling the error. * @param {function()} param.beforeComplete the callback to invoke before handling the completion. @@ -194,6 +195,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -208,6 +210,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -290,6 +293,7 @@ export default class BoltProtocol { * @param {TxConfig} param.txConfig the transaction configuration. * @param {string} param.database the target database name. * @param {string} param.impersonatedUser the impersonated user + * @param {?string[]} param.notificationFilters the filtering for notifications. * @param {string} param.mode the access mode. * @param {function(keys: string[])} param.beforeKeys the callback to invoke before handling the keys. * @param {function(keys: string[])} param.afterKeys the callback to invoke after handling the keys. @@ -309,6 +313,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeKeys, afterKeys, beforeError, @@ -332,6 +337,8 @@ export default class BoltProtocol { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // bookmarks and mode are ignored in this version of the protocol assertTxConfigIsEmpty(txConfig, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js index 503298384..d313aa636 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v3.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v3.js @@ -92,6 +92,7 @@ export default class BoltProtocol extends BoltProtocolV2 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeError, afterError, @@ -107,6 +108,8 @@ export default class BoltProtocol extends BoltProtocolV2 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error assertDatabaseIsEmpty(database, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error @@ -169,6 +172,7 @@ export default class BoltProtocol extends BoltProtocolV2 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeKeys, afterKeys, @@ -193,6 +197,8 @@ export default class BoltProtocol extends BoltProtocolV2 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error assertDatabaseIsEmpty(database, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js index 2dfc1f541..3c09d421c 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x0.js @@ -18,7 +18,7 @@ */ import BoltProtocolV3 from './bolt-protocol-v3' import RequestMessage from './request-message' -import { assertImpersonatedUserIsEmpty } from './bolt-protocol-util' +import { assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } from './bolt-protocol-util' import { ResultStreamObserver, ProcedureRouteObserver @@ -56,6 +56,7 @@ export default class BoltProtocol extends BoltProtocolV3 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeError, afterError, @@ -71,6 +72,8 @@ export default class BoltProtocol extends BoltProtocolV3 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error assertImpersonatedUserIsEmpty(impersonatedUser, this._onProtocolError, observer) @@ -91,6 +94,7 @@ export default class BoltProtocol extends BoltProtocolV3 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeKeys, afterKeys, @@ -121,6 +125,8 @@ export default class BoltProtocol extends BoltProtocolV3 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error assertImpersonatedUserIsEmpty(impersonatedUser, this._onProtocolError, observer) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js index bd2c94228..9cfc7aca3 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v4x4.js @@ -26,6 +26,8 @@ import transformersFactories from './bolt-protocol-v4x4.transformer' import utcTransformersFactories from './bolt-protocol-v5x0.utc.transformer' import Transformer from './transformer' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util' + const { constants: { BOLT_PROTOCOL_V4_4, FETCH_ALL }, bookmarks: { Bookmarks } @@ -87,6 +89,7 @@ export default class BoltProtocol extends BoltProtocolV43 { database, mode, impersonatedUser, + notificationFilters, beforeKeys, afterKeys, beforeError, @@ -116,6 +119,9 @@ export default class BoltProtocol extends BoltProtocolV43 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + const flushRun = reactive this.write( RequestMessage.runWithMetadata(query, parameters, { @@ -142,6 +148,7 @@ export default class BoltProtocol extends BoltProtocolV43 { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -156,6 +163,9 @@ export default class BoltProtocol extends BoltProtocolV43 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser }), observer, 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 9d69a2909..258cd993a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js @@ -334,6 +334,59 @@ describe('#unit BoltProtocolV1', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV1)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV1(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 a9d1f1a50..d8a50118e 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js @@ -111,6 +111,59 @@ describe('#unit BoltProtocolV2', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV2)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV2(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('watermarks', () => { it('.run() should configure watermarks', () => { const recorder = new utils.MessageRecordingConnection() 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 b3139c3cd..892a86671 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js @@ -293,6 +293,59 @@ describe('#unit BoltProtocolV3', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV3)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV3(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 0bd626c9d..12bbb0da5 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js @@ -211,6 +211,59 @@ describe('#unit BoltProtocolV4x0', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV4x0)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x0(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 c80b94f86..add96c3c1 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js @@ -85,6 +85,59 @@ describe('#unit BoltProtocolV4x1', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV4x1)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x1(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 998ac3206..8e66caa34 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js @@ -84,6 +84,60 @@ describe('#unit BoltProtocolV4x2', () => { }) }) }) + + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV4x2)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x2(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 5b2d393ff..b12292b2a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -299,6 +299,59 @@ describe('#unit BoltProtocolV4x3', () => { }) }) + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV4x3)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x3(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) + describe('unpacker configuration', () => { test.each([ [false, false], 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 8d250e7c2..24728988e 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -1128,4 +1128,57 @@ describe('#unit BoltProtocolV4x4', () => { }) }) }) + + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV4x4)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV4x4(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) }) 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 221c1893b..9b76f29c6 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -1027,4 +1027,57 @@ describe('#unit BoltProtocolV5x0', () => { expect(unpacked).toEqual(struct) }) }) + + describe('Bolt v5.1', () => { + /** + * @param {string[]} notificationFilters The impersonated user. + * @param {function(protocol: BoltProtocolV5x0)} fn + */ + function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x0(recorder, null, false, undefined, undefined, () => {}) + + expect(() => fn(protocol)).toThrowError( + 'Driver is connected to the database that does not support user notification filters. ' + + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` + ) + } + + describe('initialize', () => { + function verifyInitialize (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.initialize({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyInitialize('test') + }) + }) + + describe('beginTransaction', () => { + function verifyBeginTransaction (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.beginTransaction({ notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyBeginTransaction('test') + }) + }) + + describe('run', () => { + function verifyRun (notificationFilters) { + verifyNotificationFiltersNotSupportedError( + notificationFilters, + protocol => protocol.run('query', {}, { notificationFilters })) + } + + it('should throw error when notificationFilters is set', () => { + verifyRun('test') + }) + }) + }) }) 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 2bf061402..3a35c328d 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 @@ -182,6 +182,7 @@ export default class BoltProtocol { * @param {string} param.database the target database name. * @param {string} param.mode the access mode. * @param {string} param.impersonatedUser the impersonated user + * @param {?string[]} param.notificationFilters the filtering for notifications. * @param {function(err: Error)} param.beforeError the callback to invoke before handling the error. * @param {function(err: Error)} param.afterError the callback to invoke after handling the error. * @param {function()} param.beforeComplete the callback to invoke before handling the completion. @@ -194,6 +195,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -208,6 +210,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -290,6 +293,7 @@ export default class BoltProtocol { * @param {TxConfig} param.txConfig the transaction configuration. * @param {string} param.database the target database name. * @param {string} param.impersonatedUser the impersonated user + * @param {?string[]} param.notificationFilters the filtering for notifications. * @param {string} param.mode the access mode. * @param {function(keys: string[])} param.beforeKeys the callback to invoke before handling the keys. * @param {function(keys: string[])} param.afterKeys the callback to invoke after handling the keys. @@ -309,6 +313,7 @@ export default class BoltProtocol { database, mode, impersonatedUser, + notificationFilters, beforeKeys, afterKeys, beforeError, @@ -332,6 +337,8 @@ export default class BoltProtocol { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // bookmarks and mode are ignored in this version of the protocol assertTxConfigIsEmpty(txConfig, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error 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 20d11019d..a0fa8363b 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 @@ -92,6 +92,7 @@ export default class BoltProtocol extends BoltProtocolV2 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeError, afterError, @@ -107,6 +108,8 @@ export default class BoltProtocol extends BoltProtocolV2 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error assertDatabaseIsEmpty(database, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error @@ -169,6 +172,7 @@ export default class BoltProtocol extends BoltProtocolV2 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeKeys, afterKeys, @@ -193,6 +197,8 @@ export default class BoltProtocol extends BoltProtocolV2 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing in a database name on this protocol version throws an error assertDatabaseIsEmpty(database, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js index a9eb5c856..bc371129d 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x0.js @@ -18,7 +18,7 @@ */ import BoltProtocolV3 from './bolt-protocol-v3.js' import RequestMessage from './request-message.js' -import { assertImpersonatedUserIsEmpty } from './bolt-protocol-util.js' +import { assertImpersonatedUserIsEmpty, assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' import { ResultStreamObserver, ProcedureRouteObserver @@ -56,6 +56,7 @@ export default class BoltProtocol extends BoltProtocolV3 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeError, afterError, @@ -71,6 +72,8 @@ export default class BoltProtocol extends BoltProtocolV3 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error assertImpersonatedUserIsEmpty(impersonatedUser, this._onProtocolError, observer) @@ -91,6 +94,7 @@ export default class BoltProtocol extends BoltProtocolV3 { txConfig, database, impersonatedUser, + notificationFilters, mode, beforeKeys, afterKeys, @@ -121,6 +125,8 @@ export default class BoltProtocol extends BoltProtocolV3 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) // passing impersonated user on this protocol version throws an error assertImpersonatedUserIsEmpty(impersonatedUser, this._onProtocolError, observer) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js index 22d95f52b..5631a4e0d 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v4x4.js @@ -26,6 +26,8 @@ import transformersFactories from './bolt-protocol-v4x4.transformer.js' import utcTransformersFactories from './bolt-protocol-v5x0.utc.transformer.js' import Transformer from './transformer.js' +import { assertNotificationFiltersIsEmpty } from './bolt-protocol-util.js' + const { constants: { BOLT_PROTOCOL_V4_4, FETCH_ALL }, bookmarks: { Bookmarks } @@ -87,6 +89,7 @@ export default class BoltProtocol extends BoltProtocolV43 { database, mode, impersonatedUser, + notificationFilters, beforeKeys, afterKeys, beforeError, @@ -116,6 +119,9 @@ export default class BoltProtocol extends BoltProtocolV43 { lowRecordWatermark }) + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + const flushRun = reactive this.write( RequestMessage.runWithMetadata(query, parameters, { @@ -142,6 +148,7 @@ export default class BoltProtocol extends BoltProtocolV43 { database, mode, impersonatedUser, + notificationFilters, beforeError, afterError, beforeComplete, @@ -156,6 +163,9 @@ export default class BoltProtocol extends BoltProtocolV43 { }) observer.prepareToHandleSingleResponse() + // passing notification filters user on this protocol version throws an error + assertNotificationFiltersIsEmpty(notificationFilters, this._onProtocolError, observer) + this.write( RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser }), observer, From 9fd8e45f01ad534c482aedcc4573c692af1193e9 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 26 Oct 2022 18:14:46 +0200 Subject: [PATCH 05/27] Add BoltProtocolV5x1 with notification filters --- .../src/bolt/bolt-protocol-v5x1.js | 159 +++ .../bolt/bolt-protocol-v5x1.transformer.js | 24 + .../src/bolt/request-message.js | 23 +- .../bolt-protocol-v5x1.test.js.snap | 61 + .../test/bolt/bolt-protocol-v5x1.test.js | 1122 +++++++++++++++++ .../test/bolt/request-message.test.js | 169 +++ packages/core/src/internal/constants.ts | 4 +- .../bolt/bolt-protocol-v5x1.js | 159 +++ .../bolt/bolt-protocol-v5x1.transformer.js | 24 + .../bolt-connection/bolt/request-message.js | 23 +- .../lib/core/internal/constants.ts | 4 +- 11 files changed, 1758 insertions(+), 14 deletions(-) create mode 100644 packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js create mode 100644 packages/bolt-connection/src/bolt/bolt-protocol-v5x1.transformer.js create mode 100644 packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x1.test.js.snap create mode 100644 packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js create mode 100644 packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js create mode 100644 packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.transformer.js diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js new file mode 100644 index 000000000..a0c7586fd --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -0,0 +1,159 @@ +/** + * 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 BoltProtocolV5x0 from './bolt-protocol-v5x0' + +import transformersFactories from './bolt-protocol-v5x1.transformer' +import Transformer from './transformer' +import RequestMessage from './request-message' +import { LoginObserver, ResultStreamObserver } from './stream-observers' + +import { internal } from 'neo4j-driver-core' + +const { + constants: { BOLT_PROTOCOL_V5_1, FETCH_ALL } +} = internal + +export default class BoltProtocol extends BoltProtocolV5x0 { + get version () { + return BOLT_PROTOCOL_V5_1 + } + + 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} param0 The params + * @param {string} param0.userAgent The user agent + * @param {any} param0.authToken The auth token + * @param {function(error)} param0.onError On error callback + * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. + * @returns {LoginObserver} The Login observer + */ + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { + const observer = new LoginObserver({ + onError: error => this._onLoginError(error, onError), + onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) + }) + + this.write( + RequestMessage.hello(userAgent, authToken, this._serversideRouting, null, { notificationFilters }), + observer, + true + ) + + return observer + } + + run ( + query, + parameters, + { + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters, + beforeKeys, + afterKeys, + beforeError, + afterError, + beforeComplete, + afterComplete, + flush = true, + reactive = false, + fetchSize = FETCH_ALL, + highRecordWatermark = Number.MAX_VALUE, + lowRecordWatermark = Number.MAX_VALUE + } = {} + ) { + const observer = new ResultStreamObserver({ + server: this._server, + reactive: reactive, + fetchSize: fetchSize, + moreFunction: this._requestMore.bind(this), + discardFunction: this._requestDiscard.bind(this), + beforeKeys, + afterKeys, + beforeError, + afterError, + beforeComplete, + afterComplete, + highRecordWatermark, + lowRecordWatermark + }) + + const flushRun = reactive + this.write( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters + }), + observer, + flushRun && flush + ) + + if (!reactive) { + this.write(RequestMessage.pull({ n: fetchSize }), observer, flush) + } + + return observer + } + + beginTransaction ({ + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters, + beforeError, + afterError, + beforeComplete, + afterComplete + } = {}) { + const observer = new ResultStreamObserver({ + server: this._server, + beforeError, + afterError, + beforeComplete, + afterComplete + }) + observer.prepareToHandleSingleResponse() + + this.write( + RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters }), + observer, + true + ) + + return observer + } +} diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.transformer.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.transformer.js new file mode 100644 index 000000000..0923aef4d --- /dev/null +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.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 v5x0 from './bolt-protocol-v5x0.transformer' + +export default { + ...v5x0 +} diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index 4a525daa7..f95ddb5b5 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -104,9 +104,11 @@ export default class RequestMessage { * @param {string} userAgent the user agent. * @param {Object} authToken the authentication token. * @param {Object} optional server side routing, set to routing context to turn on server side routing (> 4.1) + * @param {?string[]} patchs patches to be applied to the server (valid in 4.3 and 4.4) + * @param {?string[]} notificationFilters the cypher notification filters * @return {RequestMessage} new HELLO message. */ - static hello (userAgent, authToken, routing = null, patchs = null) { + static hello (userAgent, authToken, routing = null, patchs = null, { notificationFilters } = {}) { const metadata = Object.assign({ user_agent: userAgent }, authToken) if (routing) { metadata.routing = routing @@ -114,6 +116,9 @@ export default class RequestMessage { if (patchs) { metadata.patch_bolt = patchs } + if (notificationFilters) { + metadata.notifications = notificationFilters + } return new RequestMessage( HELLO, [metadata], @@ -128,10 +133,11 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user. + * @param {?string[]} notificationFilters the notification filters * @return {RequestMessage} new BEGIN message. */ - static begin ({ bookmarks, txConfig, database, mode, impersonatedUser } = {}) { - const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser) + static begin ({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters } = {}) { + const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) return new RequestMessage( BEGIN, [metadata], @@ -164,14 +170,15 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user. + * @param {?string[]} notificationFilters the notification filters * @return {RequestMessage} new RUN message with additional metadata. */ static runWithMetadata ( query, parameters, - { bookmarks, txConfig, database, mode, impersonatedUser } = {} + { bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters } = {} ) { - const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser) + const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) return new RequestMessage( RUN, [query, parameters, metadata], @@ -282,9 +289,10 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user mode. + * @param {?string[]} notificationFilters the notification filters * @return {Object} a metadata object. */ -function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser) { +function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) { const metadata = {} if (!bookmarks.isEmpty()) { metadata.bookmarks = bookmarks.values() @@ -301,6 +309,9 @@ function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser) if (impersonatedUser) { metadata.imp_user = assertString(impersonatedUser, 'impersonatedUser') } + if (notificationFilters) { + metadata.notifications = notificationFilters + } if (mode === ACCESS_MODE_READ) { metadata.mode = READ_MODE } diff --git a/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x1.test.js.snap b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x1.test.js.snap new file mode 100644 index 000000000..8f5271141 --- /dev/null +++ b/packages/bolt-connection/test/bolt/__snapshots__/bolt-protocol-v5x1.test.js.snap @@ -0,0 +1,61 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`#unit BoltProtocolV5x1 .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 BoltProtocolV5x1 .packable() should pack not pack graph types (Path) 1`] = `"It is not allowed to pass paths in query parameters, given: [object Object]"`; + +exports[`#unit BoltProtocolV5x1 .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 BoltProtocolV5x1 .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 BoltProtocolV5x1 .unpack() should not unpack with wrong size (Date with less fields) 1`] = `"Wrong struct size for Date, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Date with more fields) 1`] = `"Wrong struct size for Date, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (DateTimeWithZoneId with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (DateTimeWithZoneId with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneId, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with less fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (DateTimeWithZoneOffset with more fields) 1`] = `"Wrong struct size for DateTimeWithZoneOffset, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Duration with less fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Duration with more fields) 1`] = `"Wrong struct size for Duration, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (LocalDateTime with less fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (LocalDateTime with more fields) 1`] = `"Wrong struct size for LocalDateTime, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (LocalTime with less fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 0"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (LocalTime with more fields) 1`] = `"Wrong struct size for LocalTime, expected 1 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Node with less fields) 1`] = `"Wrong struct size for Node, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Node with more fields) 1`] = `"Wrong struct size for Node, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Path with less fields) 1`] = `"Wrong struct size for Path, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Path with more fields) 1`] = `"Wrong struct size for Path, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Point with less fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 2"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Point with more fields) 1`] = `"Wrong struct size for Point2D, expected 3 but was 4"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Point3D with less fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Point3D with more fields) 1`] = `"Wrong struct size for Point3D, expected 4 but was 5"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Relationship with less fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 5"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Relationship with more fields) 1`] = `"Wrong struct size for Relationship, expected 8 but was 9"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Time with less fields) 1`] = `"Wrong struct size for Time, expected 2 but was 1"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (Time with more fileds) 1`] = `"Wrong struct size for Time, expected 2 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .unpack() should not unpack with wrong size (UnboundRelationship with less fields) 1`] = `"Wrong struct size for UnboundRelationship, expected 4 but was 3"`; + +exports[`#unit BoltProtocolV5x1 .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-v5x1.test.js b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js new file mode 100644 index 000000000..1c50b129f --- /dev/null +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -0,0 +1,1122 @@ +/** + * 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 BoltProtocolV5x1 from '../../src/bolt/bolt-protocol-v5x1' +import RequestMessage from '../../src/bolt/request-message' +import { v2, structure } from '../../src/packstream' +import utils from '../test-utils' +import { 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' + +const WRITE = 'WRITE' + +const { + txConfig: { TxConfig }, + bookmarks: { Bookmarks }, + logger: { Logger }, + temporalUtil +} = internal + +describe('#unit BoltProtocolV5x1', () => { + beforeEach(() => { + expect.extend(utils.matchers) + }) + + it('should request routing information', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = new BoltProtocolV5x1(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 BoltProtocolV5x1(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 BoltProtocolV5x1(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 BoltProtocolV5x1(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 run a with notification filters', () => { + const database = 'testdb' + const notificationFilters = ['*.*', 'WARNING.*'] + 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 BoltProtocolV5x1(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, + notificationFilters + }) + + protocol.verifyMessageCount(2) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE, + notificationFilters + }) + ) + 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 BoltProtocolV5x1(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 BoltProtocolV5x1(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 begin a transaction with notification filters', () => { + const database = 'testdb' + const notificationFilters = ['*.*', 'WARNING.*'] + 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 BoltProtocolV5x1(recorder, null, false) + utils.spyProtocolWrite(protocol) + + const observer = protocol.beginTransaction({ + bookmarks, + txConfig, + database, + mode: WRITE, + notificationFilters + }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.begin({ bookmarks, txConfig, database, mode: WRITE, notificationFilters }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should return correct bolt version number', () => { + const protocol = new BoltProtocolV5x1(null, null, false) + + expect(protocol.version).toBe(5.1) + }) + + it('should update metadata', () => { + const metadata = { t_first: 1, t_last: 2, db_hits: 3, some_other_key: 4 } + const protocol = new BoltProtocolV5x1(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 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: clientName, authToken }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(clientName, authToken) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + it('should initialize connection with notification filters', () => { + 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 notificationFilters = ['*.*', 'WARNING.*'] + + const observer = protocol.initialize({ userAgent: clientName, authToken, notificationFilters }) + + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello(clientName, authToken, false, null, { notificationFilters }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) + + 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 BoltProtocolV5x1(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 BoltProtocolV5x1(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 BoltProtocolV5x1(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]) + }) + + 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 BoltProtocolV5x1(null, null, { + disableLosslessIntegers, + useBigInt + }) + expect(protocol._unpacker._disableLosslessIntegers).toBe( + disableLosslessIntegers + ) + expect(protocol._unpacker._useBigInt).toBe(useBigInt) + } + ) + }) + + describe('watermarks', () => { + it('.run() should configure watermarks', () => { + const recorder = new utils.MessageRecordingConnection() + const protocol = utils.spyProtocolWrite( + new BoltProtocolV5x1(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 BoltProtocolV5x1(null, null, false) + expect(protocol.packer()).toBeInstanceOf(v2.Packer) + }) + + it('should configure v2 unpacker', () => { + const protocol = new BoltProtocolV5x1(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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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 BoltProtocolV5x1( + 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) + }) + }) +}) diff --git a/packages/bolt-connection/test/bolt/request-message.test.js b/packages/bolt-connection/test/bolt/request-message.test.js index f0f66dd68..c865a6b22 100644 --- a/packages/bolt-connection/test/bolt/request-message.test.js +++ b/packages/bolt-connection/test/bolt/request-message.test.js @@ -429,4 +429,173 @@ describe('#unit RequestMessage', () => { }) }) }) + + describe('BoltV5.1', () => { + it('should create HELLO message with notification filters', () => { + const userAgent = 'my-driver/1.0.2' + const authToken = { username: 'neo4j', password: 'secret' } + const notificationFilters = ['*.*', 'WARNING.*'] + + const message = RequestMessage.hello(userAgent, authToken, null, null, { notificationFilters }) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + { user_agent: userAgent, username: 'neo4j', password: 'secret', notifications: notificationFilters } + ]) + expect(message.toString()).toEqual( + `HELLO {user_agent: '${userAgent}', ...}` + ) + }) + + it('should create HELLO message without notification filters if it is not supplied or null', () => { + ;[null, undefined].forEach(notificationFilters => { + const userAgent = 'my-driver/1.0.2' + const authToken = { username: 'neo4j', password: 'secret' } + + const message = RequestMessage.hello(userAgent, authToken, null, null, { notificationFilters }) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + { user_agent: userAgent, username: 'neo4j', password: 'secret' } + ]) + expect(message.toString()).toEqual( + `HELLO {user_agent: '${userAgent}', ...}` + ) + }) + }) + + it('should create BEGIN message with notification filters', () => { + ;[READ, WRITE].forEach(mode => { + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10' + ]) + const notificationFilters = ['*.*', 'WARNING.*'] + const txConfig = new TxConfig({ timeout: 42, metadata: { key: 42 } }) + + const message = RequestMessage.begin({ bookmarks, txConfig, mode, notificationFilters }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(42), + tx_metadata: { key: 42 }, + notifications: notificationFilters + } + if (mode === READ) { + expectedMetadata.mode = 'r' + } + + expect(message.signature).toEqual(0x11) + expect(message.fields).toEqual([expectedMetadata]) + expect(message.toString()).toEqual( + `BEGIN ${json.stringify(expectedMetadata)}` + ) + }) + }) + + it('should create BEGIN message without notification filters if it is not supplied or null', () => { + ;[undefined, null].forEach(notificationFilters => { + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10' + ]) + const mode = WRITE + const txConfig = new TxConfig({ timeout: 42, metadata: { key: 42 } }) + + const message = RequestMessage.begin({ bookmarks, txConfig, mode, notificationFilters }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(42), + tx_metadata: { key: 42 } + } + + expect(message.signature).toEqual(0x11) + expect(message.fields).toEqual([expectedMetadata]) + expect(message.toString()).toEqual( + `BEGIN ${json.stringify(expectedMetadata)}` + ) + }) + }) + + it('should create RUN message with the notification filters', () => { + ;[READ, WRITE].forEach(mode => { + const query = 'RETURN $x' + const parameters = { x: 42 } + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10', + 'neo4j:bookmark:v1:tx100' + ]) + const txConfig = new TxConfig({ + timeout: 999, + metadata: { a: 'a', b: 'b' } + }) + const notificationFilters = ['*.*', 'WARNING.*'] + + const message = RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + mode, + notificationFilters + }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(999), + tx_metadata: { a: 'a', b: 'b' }, + notifications: notificationFilters + } + if (mode === READ) { + expectedMetadata.mode = 'r' + } + + expect(message.signature).toEqual(0x10) + expect(message.fields).toEqual([query, parameters, expectedMetadata]) + expect(message.toString()).toEqual( + `RUN ${query} ${json.stringify(parameters)} ${json.stringify( + expectedMetadata + )}` + ) + }) + }) + + it('should create RUN message without notification filters if it is not supplied or null', () => { + ;[undefined, null].forEach(notificationFilters => { + const mode = WRITE + const query = 'RETURN $x' + const parameters = { x: 42 } + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10', + 'neo4j:bookmark:v1:tx100' + ]) + const txConfig = new TxConfig({ + timeout: 999, + metadata: { a: 'a', b: 'b' } + }) + + const message = RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + mode, + notificationFilters + }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(999), + tx_metadata: { a: 'a', b: 'b' } + } + + expect(message.signature).toEqual(0x10) + expect(message.fields).toEqual([query, parameters, expectedMetadata]) + expect(message.toString()).toEqual( + `RUN ${query} ${json.stringify(parameters)} ${json.stringify( + expectedMetadata + )}` + ) + }) + }) + }) }) diff --git a/packages/core/src/internal/constants.ts b/packages/core/src/internal/constants.ts index 39e790f2f..1b9519387 100644 --- a/packages/core/src/internal/constants.ts +++ b/packages/core/src/internal/constants.ts @@ -34,6 +34,7 @@ const BOLT_PROTOCOL_V4_2: number = 4.2 const BOLT_PROTOCOL_V4_3: number = 4.3 const BOLT_PROTOCOL_V4_4: number = 4.4 const BOLT_PROTOCOL_V5_0: number = 5.0 +const BOLT_PROTOCOL_V5_1: number = 5.1 export { FETCH_ALL, @@ -50,5 +51,6 @@ export { BOLT_PROTOCOL_V4_2, BOLT_PROTOCOL_V4_3, BOLT_PROTOCOL_V4_4, - BOLT_PROTOCOL_V5_0 + BOLT_PROTOCOL_V5_0, + BOLT_PROTOCOL_V5_1 } 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 new file mode 100644 index 000000000..36a3d14f6 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.js @@ -0,0 +1,159 @@ +/** + * 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 BoltProtocolV5x0 from './bolt-protocol-v5x0.js' + +import transformersFactories from './bolt-protocol-v5x1.transformer.js' +import Transformer from './transformer.js' +import RequestMessage from './request-message.js' +import { LoginObserver, ResultStreamObserver } from './stream-observers.js' + +import { internal } from '../../core/index.ts' + +const { + constants: { BOLT_PROTOCOL_V5_1, FETCH_ALL } +} = internal + +export default class BoltProtocol extends BoltProtocolV5x0 { + get version () { + return BOLT_PROTOCOL_V5_1 + } + + 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} param0 The params + * @param {string} param0.userAgent The user agent + * @param {any} param0.authToken The auth token + * @param {function(error)} param0.onError On error callback + * @param {function(onComplte)} param0.onComplete On complete callback + * @param {?string[]} param0.notificationFilters the filtering for notifications. + * @returns {LoginObserver} The Login observer + */ + initialize ({ userAgent, authToken, onError, onComplete, notificationFilters } = {}) { + const observer = new LoginObserver({ + onError: error => this._onLoginError(error, onError), + onCompleted: metadata => this._onLoginCompleted(metadata, onComplete) + }) + + this.write( + RequestMessage.hello(userAgent, authToken, this._serversideRouting, null, { notificationFilters }), + observer, + true + ) + + return observer + } + + run ( + query, + parameters, + { + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters, + beforeKeys, + afterKeys, + beforeError, + afterError, + beforeComplete, + afterComplete, + flush = true, + reactive = false, + fetchSize = FETCH_ALL, + highRecordWatermark = Number.MAX_VALUE, + lowRecordWatermark = Number.MAX_VALUE + } = {} + ) { + const observer = new ResultStreamObserver({ + server: this._server, + reactive: reactive, + fetchSize: fetchSize, + moreFunction: this._requestMore.bind(this), + discardFunction: this._requestDiscard.bind(this), + beforeKeys, + afterKeys, + beforeError, + afterError, + beforeComplete, + afterComplete, + highRecordWatermark, + lowRecordWatermark + }) + + const flushRun = reactive + this.write( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters + }), + observer, + flushRun && flush + ) + + if (!reactive) { + this.write(RequestMessage.pull({ n: fetchSize }), observer, flush) + } + + return observer + } + + beginTransaction ({ + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters, + beforeError, + afterError, + beforeComplete, + afterComplete + } = {}) { + const observer = new ResultStreamObserver({ + server: this._server, + beforeError, + afterError, + beforeComplete, + afterComplete + }) + observer.prepareToHandleSingleResponse() + + this.write( + RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters }), + observer, + true + ) + + return observer + } +} diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.transformer.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.transformer.js new file mode 100644 index 000000000..b8583e846 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-v5x1.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 v5x0 from './bolt-protocol-v5x0.transformer.js' + +export default { + ...v5x0 +} 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 0ceb430c0..8dde1b7e9 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 @@ -104,9 +104,11 @@ export default class RequestMessage { * @param {string} userAgent the user agent. * @param {Object} authToken the authentication token. * @param {Object} optional server side routing, set to routing context to turn on server side routing (> 4.1) + * @param {?string[]} patchs patches to be applied to the server (valid in 4.3 and 4.4) + * @param {?string[]} notificationFilters the cypher notification filters * @return {RequestMessage} new HELLO message. */ - static hello (userAgent, authToken, routing = null, patchs = null) { + static hello (userAgent, authToken, routing = null, patchs = null, { notificationFilters } = {}) { const metadata = Object.assign({ user_agent: userAgent }, authToken) if (routing) { metadata.routing = routing @@ -114,6 +116,9 @@ export default class RequestMessage { if (patchs) { metadata.patch_bolt = patchs } + if (notificationFilters) { + metadata.notifications = notificationFilters + } return new RequestMessage( HELLO, [metadata], @@ -128,10 +133,11 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user. + * @param {?string[]} notificationFilters the notification filters * @return {RequestMessage} new BEGIN message. */ - static begin ({ bookmarks, txConfig, database, mode, impersonatedUser } = {}) { - const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser) + static begin ({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters } = {}) { + const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) return new RequestMessage( BEGIN, [metadata], @@ -164,14 +170,15 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user. + * @param {?string[]} notificationFilters the notification filters * @return {RequestMessage} new RUN message with additional metadata. */ static runWithMetadata ( query, parameters, - { bookmarks, txConfig, database, mode, impersonatedUser } = {} + { bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters } = {} ) { - const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser) + const metadata = buildTxMetadata(bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) return new RequestMessage( RUN, [query, parameters, metadata], @@ -282,9 +289,10 @@ export default class RequestMessage { * @param {string} database the database name. * @param {string} mode the access mode. * @param {string} impersonatedUser the impersonated user mode. + * @param {?string[]} notificationFilters the notification filters * @return {Object} a metadata object. */ -function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser) { +function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters) { const metadata = {} if (!bookmarks.isEmpty()) { metadata.bookmarks = bookmarks.values() @@ -301,6 +309,9 @@ function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser) if (impersonatedUser) { metadata.imp_user = assertString(impersonatedUser, 'impersonatedUser') } + if (notificationFilters) { + metadata.notifications = notificationFilters + } if (mode === ACCESS_MODE_READ) { metadata.mode = READ_MODE } diff --git a/packages/neo4j-driver-deno/lib/core/internal/constants.ts b/packages/neo4j-driver-deno/lib/core/internal/constants.ts index 39e790f2f..1b9519387 100644 --- a/packages/neo4j-driver-deno/lib/core/internal/constants.ts +++ b/packages/neo4j-driver-deno/lib/core/internal/constants.ts @@ -34,6 +34,7 @@ const BOLT_PROTOCOL_V4_2: number = 4.2 const BOLT_PROTOCOL_V4_3: number = 4.3 const BOLT_PROTOCOL_V4_4: number = 4.4 const BOLT_PROTOCOL_V5_0: number = 5.0 +const BOLT_PROTOCOL_V5_1: number = 5.1 export { FETCH_ALL, @@ -50,5 +51,6 @@ export { BOLT_PROTOCOL_V4_2, BOLT_PROTOCOL_V4_3, BOLT_PROTOCOL_V4_4, - BOLT_PROTOCOL_V5_0 + BOLT_PROTOCOL_V5_0, + BOLT_PROTOCOL_V5_1 } From c2e9bcb63bae33b86f9309ff7448b67fae2d21fe Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 26 Oct 2022 18:23:33 +0200 Subject: [PATCH 06/27] Add bolt 5.1 to handshake --- packages/bolt-connection/src/bolt/create.js | 11 +++++++++++ packages/bolt-connection/src/bolt/handshake.js | 2 +- packages/bolt-connection/test/bolt/index.test.js | 8 +++++--- .../lib/bolt-connection/bolt/create.js | 11 +++++++++++ .../lib/bolt-connection/bolt/handshake.js | 2 +- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/packages/bolt-connection/src/bolt/create.js b/packages/bolt-connection/src/bolt/create.js index f2f4065b3..374266fda 100644 --- a/packages/bolt-connection/src/bolt/create.js +++ b/packages/bolt-connection/src/bolt/create.js @@ -27,6 +27,7 @@ import BoltProtocolV4x2 from './bolt-protocol-v4x2' import BoltProtocolV4x3 from './bolt-protocol-v4x3' import BoltProtocolV4x4 from './bolt-protocol-v4x4' import BoltProtocolV5x0 from './bolt-protocol-v5x0' +import BoltProtocolV5x1 from './bolt-protocol-v5x1' // eslint-disable-next-line no-unused-vars import { Chunker, Dechunker } from '../channel' import ResponseHandler from './response-handler' @@ -191,6 +192,16 @@ function createProtocol ( onProtocolError, serversideRouting ) + case 5.1: + return new BoltProtocolV5x1( + 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 c41d4bd82..09eb4beff 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, 0), + [version(5, 1), version(5, 0)], [version(4, 4), version(4, 2)], version(4, 1), version(3, 0) diff --git a/packages/bolt-connection/test/bolt/index.test.js b/packages/bolt-connection/test/bolt/index.test.js index 4542c6008..2860f4ef7 100644 --- a/packages/bolt-connection/test/bolt/index.test.js +++ b/packages/bolt-connection/test/bolt/index.test.js @@ -31,6 +31,7 @@ import BoltProtocolV4x2 from '../../src/bolt/bolt-protocol-v4x2' import BoltProtocolV4x3 from '../../src/bolt/bolt-protocol-v4x3' import BoltProtocolV4x4 from '../../src/bolt/bolt-protocol-v4x4' import BoltProtocolV5x0 from '../../src/bolt/bolt-protocol-v5x0' +import BoltProtocolV5x1 from '../../src/bolt/bolt-protocol-v5x1' const { logger: { Logger } @@ -44,13 +45,13 @@ describe('#unit Bolt', () => { const writtenBuffer = channel.written[0] const boltMagicPreamble = '60 60 b0 17' - const protocolVersion5x0 = '00 00 00 05' + const protocolVersion5x1to5x0 = '00 01 01 05' const protocolVersion4x4to4x2 = '00 02 04 04' const protocolVersion4x1 = '00 00 01 04' const protocolVersion3 = '00 00 00 03' expect(writtenBuffer.toHex()).toEqual( - `${boltMagicPreamble} ${protocolVersion5x0} ${protocolVersion4x4to4x2} ${protocolVersion4x1} ${protocolVersion3}` + `${boltMagicPreamble} ${protocolVersion5x1to5x0} ${protocolVersion4x4to4x2} ${protocolVersion4x1} ${protocolVersion3}` ) }) @@ -359,7 +360,8 @@ describe('#unit Bolt', () => { v(4.2, BoltProtocolV4x2), v(4.3, BoltProtocolV4x3), v(4.4, BoltProtocolV4x4), - v(5.0, BoltProtocolV5x0) + v(5.0, BoltProtocolV5x0), + v(5.1, BoltProtocolV5x1) ] availableProtocols.forEach(lambda) 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 9a4549cd6..45c33eef2 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/create.js @@ -27,6 +27,7 @@ import BoltProtocolV4x2 from './bolt-protocol-v4x2.js' import BoltProtocolV4x3 from './bolt-protocol-v4x3.js' import BoltProtocolV4x4 from './bolt-protocol-v4x4.js' import BoltProtocolV5x0 from './bolt-protocol-v5x0.js' +import BoltProtocolV5x1 from './bolt-protocol-v5x1.js' // eslint-disable-next-line no-unused-vars import { Chunker, Dechunker } from '../channel/index.js' import ResponseHandler from './response-handler.js' @@ -191,6 +192,16 @@ function createProtocol ( onProtocolError, serversideRouting ) + case 5.1: + return new BoltProtocolV5x1( + 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 515458ee5..f8c0de714 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, 0), + [version(5, 1), version(5, 0)], [version(4, 4), version(4, 2)], version(4, 1), version(3, 0) From 3899d8deeab58945ff21abe5b5aae8f2b5881e74 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Wed, 26 Oct 2022 18:55:15 +0200 Subject: [PATCH 07/27] Add notification filters to Connection.initialize --- .../connection-provider-pooled.js | 5 +++-- .../src/connection/connection-channel.js | 17 +++++++++------ .../src/connection/connection-delegate.js | 4 ++-- .../src/connection/connection.js | 3 ++- .../connection/connection-channel.test.js | 21 +++++++++++++++++++ .../connection-provider-pooled.js | 5 +++-- .../connection/connection-channel.js | 17 +++++++++------ .../connection/connection-delegate.js | 4 ++-- .../bolt-connection/connection/connection.js | 3 ++- 9 files changed, 57 insertions(+), 22 deletions(-) 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 42cb30fbc..0c5067013 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js @@ -24,7 +24,7 @@ import { error, ConnectionProvider, ServerInfo } from 'neo4j-driver-core' const { SERVICE_UNAVAILABLE } = error export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authToken }, + { id, config, log, userAgent, authToken, notificationFilters }, createChannelConnectionHook = null ) { super() @@ -34,6 +34,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._log = log this._userAgent = userAgent this._authToken = authToken + this._notificationFilters = notificationFilters this._createChannelConnection = createChannelConnectionHook || (address => { @@ -76,7 +77,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { } this._openConnections[connection.id] = connection return connection - .connect(this._userAgent, this._authToken) + .connect(this._userAgent, this._authToken, this._notificationFilters) .catch(error => { // let's destroy this connection this._destroyConnection(connection) diff --git a/packages/bolt-connection/src/connection/connection-channel.js b/packages/bolt-connection/src/connection/connection-channel.js index 4efcb5f51..f1fc0e516 100644 --- a/packages/bolt-connection/src/connection/connection-channel.js +++ b/packages/bolt-connection/src/connection/connection-channel.js @@ -169,24 +169,27 @@ export default class ChannelConnection extends Connection { * Send initialization message. * @param {string} userAgent the user agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { - return this._initialize(userAgent, authToken) + connect (userAgent, authToken, notificationFilters) { + return this._initialize(userAgent, authToken, notificationFilters) } /** * Perform protocol-specific initialization which includes authentication. * @param {string} userAgent the user agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if initialization is successful. Rejected promise otherwise. */ - _initialize (userAgent, authToken) { + _initialize (userAgent, authToken, notificationFilters) { const self = this return new Promise((resolve, reject) => { this._protocol.initialize({ userAgent, authToken, + notificationFilters, onError: err => reject(err), onComplete: metadata => { if (metadata) { @@ -340,13 +343,14 @@ export default class ChannelConnection extends Connection { }) } - _reset(observer) { + _reset (observer) { if (this._reseting) { if (!this._protocol.isLastMessageReset()) { this._protocol.reset({ onError: error => { observer.onError(error) - }, onComplete: () => { + }, + onComplete: () => { observer.onComplete() } }) @@ -369,7 +373,8 @@ export default class ChannelConnection extends Connection { this._protocol.reset({ onError: error => { notifyFinish(obs => obs.onError(error)) - }, onComplete: () => { + }, + onComplete: () => { notifyFinish(obs => obs.onComplete()) } }) diff --git a/packages/bolt-connection/src/connection/connection-delegate.js b/packages/bolt-connection/src/connection/connection-delegate.js index 16d2da48a..9723962fa 100644 --- a/packages/bolt-connection/src/connection/connection-delegate.js +++ b/packages/bolt-connection/src/connection/connection-delegate.js @@ -71,8 +71,8 @@ export default class DelegateConnection extends Connection { return this._delegate.protocol() } - connect (userAgent, authToken) { - return this._delegate.connect(userAgent, authToken) + connect (userAgent, authToken, notificationFilters) { + return this._delegate.connect(userAgent, authToken, notificationFilters) } write (message, observer, flush) { diff --git a/packages/bolt-connection/src/connection/connection.js b/packages/bolt-connection/src/connection/connection.js index d3c692712..6d919d7a8 100644 --- a/packages/bolt-connection/src/connection/connection.js +++ b/packages/bolt-connection/src/connection/connection.js @@ -79,9 +79,10 @@ 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 {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { + connect (userAgent, authToken, notificationFilters) { throw new Error('not implemented') } diff --git a/packages/bolt-connection/test/connection/connection-channel.test.js b/packages/bolt-connection/test/connection/connection-channel.test.js index 21696bd27..e3cf80a46 100644 --- a/packages/bolt-connection/test/connection/connection-channel.test.js +++ b/packages/bolt-connection/test/connection/connection-channel.test.js @@ -124,6 +124,27 @@ describe('ChannelConnection', () => { ) } ) + + it.each([ + undefined, + null, + [], + ['*.*', 'WARNING.*'] + ])('should call protocol.initialize with notification filters when notificationFilters is %o', + async (notificationFilters) => { + const protocol = { + initialize: jest.fn(observer => observer.onComplete({})) + } + const protocolSupplier = () => protocol + const connection = spyOnConnectionChannel({ protocolSupplier }) + + await connection.connect('userAgent', {}, notificationFilters) + + expect(protocol.initialize).toHaveBeenCalledTimes(1) + expect(protocol.initialize).toBeCalledWith(expect.objectContaining({ + notificationFilters + })) + }) }) describe('._handleFatalError()', () => { 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 208cbd585..67c53f594 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 @@ -24,7 +24,7 @@ import { error, ConnectionProvider, ServerInfo } from '../../core/index.ts' const { SERVICE_UNAVAILABLE } = error export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authToken }, + { id, config, log, userAgent, authToken, notificationFilters }, createChannelConnectionHook = null ) { super() @@ -34,6 +34,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._log = log this._userAgent = userAgent this._authToken = authToken + this._notificationFilters = notificationFilters this._createChannelConnection = createChannelConnectionHook || (address => { @@ -76,7 +77,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { } this._openConnections[connection.id] = connection return connection - .connect(this._userAgent, this._authToken) + .connect(this._userAgent, this._authToken, this._notificationFilters) .catch(error => { // let's destroy this connection this._destroyConnection(connection) 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 8dbcbc509..84af8c3d2 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 @@ -169,24 +169,27 @@ export default class ChannelConnection extends Connection { * Send initialization message. * @param {string} userAgent the user agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { - return this._initialize(userAgent, authToken) + connect (userAgent, authToken, notificationFilters) { + return this._initialize(userAgent, authToken, notificationFilters) } /** * Perform protocol-specific initialization which includes authentication. * @param {string} userAgent the user agent for this driver. * @param {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if initialization is successful. Rejected promise otherwise. */ - _initialize (userAgent, authToken) { + _initialize (userAgent, authToken, notificationFilters) { const self = this return new Promise((resolve, reject) => { this._protocol.initialize({ userAgent, authToken, + notificationFilters, onError: err => reject(err), onComplete: metadata => { if (metadata) { @@ -340,13 +343,14 @@ export default class ChannelConnection extends Connection { }) } - _reset(observer) { + _reset (observer) { if (this._reseting) { if (!this._protocol.isLastMessageReset()) { this._protocol.reset({ onError: error => { observer.onError(error) - }, onComplete: () => { + }, + onComplete: () => { observer.onComplete() } }) @@ -369,7 +373,8 @@ export default class ChannelConnection extends Connection { this._protocol.reset({ onError: error => { notifyFinish(obs => obs.onError(error)) - }, onComplete: () => { + }, + onComplete: () => { notifyFinish(obs => obs.onComplete()) } }) 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 6d195d1d9..36b2694d3 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 @@ -71,8 +71,8 @@ export default class DelegateConnection extends Connection { return this._delegate.protocol() } - connect (userAgent, authToken) { - return this._delegate.connect(userAgent, authToken) + connect (userAgent, authToken, notificationFilters) { + return this._delegate.connect(userAgent, authToken, notificationFilters) } 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 dc996522c..fa65045f0 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/connection/connection.js @@ -79,9 +79,10 @@ 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 {Object} authToken the object containing auth information. + * @param {?string[]} notificationFilters the notification filters. * @return {Promise} promise resolved with the current connection if connection is successful. Rejected promise otherwise. */ - connect (userAgent, authToken) { + connect (userAgent, authToken, notificationFilters) { throw new Error('not implemented') } From ef4320c8148cb2b90bd9bd416734a6d77ef087c7 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 27 Oct 2022 13:18:00 +0200 Subject: [PATCH 08/27] Add notification configuration to session and driver --- packages/core/src/driver.ts | 21 +++++++++++++++---- packages/core/src/session.ts | 10 +++++++-- packages/core/src/transaction-promise.ts | 7 +++++-- packages/core/src/transaction.ts | 7 ++++++- packages/neo4j-driver-deno/lib/core/driver.ts | 21 +++++++++++++++---- .../neo4j-driver-deno/lib/core/session.ts | 10 +++++++-- .../lib/core/transaction-promise.ts | 7 +++++-- .../neo4j-driver-deno/lib/core/transaction.ts | 7 ++++++- packages/neo4j-driver/src/driver.js | 6 ++++-- 9 files changed, 76 insertions(+), 20 deletions(-) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 3c07d3c44..3b8082f79 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -89,6 +89,7 @@ type CreateSession = (args: { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager + notificationFilters?: string[] }) => Session interface DriverConfig { @@ -96,6 +97,7 @@ interface DriverConfig { trust?: TrustStrategy fetchSize?: number logging?: LoggingConfig + notificationFilters?: string[] } /** @@ -110,6 +112,7 @@ class SessionConfig { impersonatedUser?: string fetchSize?: number bookmarkManager?: BookmarkManager + notificationFilters?: string[] /** * @constructor @@ -190,6 +193,11 @@ class SessionConfig { * @since 5.0 */ this.bookmarkManager = undefined + + /** + * @todo docs + */ + this.notificationFilters = undefined } } @@ -388,7 +396,8 @@ class Driver { database = '', impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }: SessionConfig = {}): Session { return this._newSession({ defaultAccessMode, @@ -398,7 +407,8 @@ class Driver { impersonatedUser, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion fetchSize: validateFetchSizeValue(fetchSize, this._config.fetchSize!), - bookmarkManager + bookmarkManager, + notificationFilters }) } @@ -435,7 +445,8 @@ class Driver { reactive, impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }: { defaultAccessMode: SessionMode bookmarkOrBookmarks?: string | string[] @@ -444,6 +455,7 @@ class Driver { impersonatedUser?: string fetchSize: number bookmarkManager?: BookmarkManager + notificationFilters?: string[] }): Session { const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() @@ -460,7 +472,8 @@ class Driver { reactive, impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }) } diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index 09c5a1892..b3927c1c6 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -72,6 +72,7 @@ class Session { private readonly _highRecordWatermark: number private readonly _results: Result[] private readonly _bookmarkManager?: BookmarkManager + private readonly _notificationFilters?: string[] /** * @constructor * @protected @@ -94,7 +95,8 @@ class Session { reactive, fetchSize, impersonatedUser, - bookmarkManager + bookmarkManager, + notificationFilters }: { mode: SessionMode connectionProvider: ConnectionProvider @@ -105,6 +107,7 @@ class Session { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager + notificationFilters?: string[] }) { this._mode = mode this._database = database @@ -142,6 +145,7 @@ class Session { this._highRecordWatermark = calculatedWatermaks.high this._results = [] this._bookmarkManager = bookmarkManager + this._notificationFilters = notificationFilters } /** @@ -181,7 +185,8 @@ class Session { reactive: this._reactive, fetchSize: this._fetchSize, lowRecordWatermark: this._lowRecordWatermark, - highRecordWatermark: this._highRecordWatermark + highRecordWatermark: this._highRecordWatermark, + notificationFilters: this._notificationFilters }) }) this._results.push(result) @@ -292,6 +297,7 @@ class Session { const tx = new TransactionPromise({ connectionHolder, impersonatedUser: this._impersonatedUser, + notificationFilters: this._notificationFilters, onClose: this._transactionClosed.bind(this), onBookmarks: (newBm, oldBm, db) => this._updateBookmarks(newBm, oldBm, db), onConnection: this._assertSessionIsOpen.bind(this), diff --git a/packages/core/src/transaction-promise.ts b/packages/core/src/transaction-promise.ts index 04798195c..2ace08797 100644 --- a/packages/core/src/transaction-promise.ts +++ b/packages/core/src/transaction-promise.ts @@ -65,7 +65,8 @@ class TransactionPromise extends Transaction implements Promise { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }: { connectionHolder: ConnectionHolder onClose: () => void @@ -76,6 +77,7 @@ class TransactionPromise extends Transaction implements Promise { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number + notificationFilters?: string[] }) { super({ connectionHolder, @@ -86,7 +88,8 @@ class TransactionPromise extends Transaction implements Promise { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }) } diff --git a/packages/core/src/transaction.ts b/packages/core/src/transaction.ts index 3bee6d480..2750f91a8 100644 --- a/packages/core/src/transaction.ts +++ b/packages/core/src/transaction.ts @@ -61,6 +61,7 @@ class Transaction { private _bookmarks: Bookmarks private readonly _activePromise: Promise private _acceptActive: () => void + private readonly _notificationFilters?: string[] /** * @constructor @@ -84,7 +85,8 @@ class Transaction { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }: { connectionHolder: ConnectionHolder onClose: () => void @@ -95,6 +97,7 @@ class Transaction { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number + notificationFilters?: string[] }) { this._connectionHolder = connectionHolder this._reactive = reactive @@ -110,6 +113,7 @@ class Transaction { this._lowRecordWatermak = lowRecordWatermark this._highRecordWatermark = highRecordWatermark this._bookmarks = Bookmarks.empty() + this._notificationFilters = notificationFilters this._acceptActive = () => { } // satisfy DenoJS this._activePromise = new Promise((resolve, reject) => { this._acceptActive = resolve @@ -138,6 +142,7 @@ class Transaction { mode: this._connectionHolder.mode(), database: this._connectionHolder.database(), impersonatedUser: this._impersonatedUser, + notificationFilters: this._notificationFilters, beforeError: (error: Error) => { if (events != null) { events.onError(error) diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index cde7a6865..809baa221 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -89,6 +89,7 @@ type CreateSession = (args: { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager + notificationFilters?: string[] }) => Session interface DriverConfig { @@ -96,6 +97,7 @@ interface DriverConfig { trust?: TrustStrategy fetchSize?: number logging?: LoggingConfig + notificationFilters?: string[] } /** @@ -110,6 +112,7 @@ class SessionConfig { impersonatedUser?: string fetchSize?: number bookmarkManager?: BookmarkManager + notificationFilters?: string[] /** * @constructor @@ -190,6 +193,11 @@ class SessionConfig { * @since 5.0 */ this.bookmarkManager = undefined + + /** + * @todo docs + */ + this.notificationFilters = undefined } } @@ -388,7 +396,8 @@ class Driver { database = '', impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }: SessionConfig = {}): Session { return this._newSession({ defaultAccessMode, @@ -398,7 +407,8 @@ class Driver { impersonatedUser, // eslint-disable-next-line @typescript-eslint/no-non-null-assertion fetchSize: validateFetchSizeValue(fetchSize, this._config.fetchSize!), - bookmarkManager + bookmarkManager, + notificationFilters }) } @@ -435,7 +445,8 @@ class Driver { reactive, impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }: { defaultAccessMode: SessionMode bookmarkOrBookmarks?: string | string[] @@ -444,6 +455,7 @@ class Driver { impersonatedUser?: string fetchSize: number bookmarkManager?: BookmarkManager + notificationFilters?: string[] }): Session { const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() @@ -460,7 +472,8 @@ class Driver { reactive, impersonatedUser, fetchSize, - bookmarkManager + bookmarkManager, + notificationFilters }) } diff --git a/packages/neo4j-driver-deno/lib/core/session.ts b/packages/neo4j-driver-deno/lib/core/session.ts index fd6770345..f4ae2d5c5 100644 --- a/packages/neo4j-driver-deno/lib/core/session.ts +++ b/packages/neo4j-driver-deno/lib/core/session.ts @@ -72,6 +72,7 @@ class Session { private readonly _highRecordWatermark: number private readonly _results: Result[] private readonly _bookmarkManager?: BookmarkManager + private readonly _notificationFilters?: string[] /** * @constructor * @protected @@ -94,7 +95,8 @@ class Session { reactive, fetchSize, impersonatedUser, - bookmarkManager + bookmarkManager, + notificationFilters }: { mode: SessionMode connectionProvider: ConnectionProvider @@ -105,6 +107,7 @@ class Session { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager + notificationFilters?: string[] }) { this._mode = mode this._database = database @@ -142,6 +145,7 @@ class Session { this._highRecordWatermark = calculatedWatermaks.high this._results = [] this._bookmarkManager = bookmarkManager + this._notificationFilters = notificationFilters } /** @@ -181,7 +185,8 @@ class Session { reactive: this._reactive, fetchSize: this._fetchSize, lowRecordWatermark: this._lowRecordWatermark, - highRecordWatermark: this._highRecordWatermark + highRecordWatermark: this._highRecordWatermark, + notificationFilters: this._notificationFilters }) }) this._results.push(result) @@ -292,6 +297,7 @@ class Session { const tx = new TransactionPromise({ connectionHolder, impersonatedUser: this._impersonatedUser, + notificationFilters: this._notificationFilters, onClose: this._transactionClosed.bind(this), onBookmarks: (newBm, oldBm, db) => this._updateBookmarks(newBm, oldBm, db), onConnection: this._assertSessionIsOpen.bind(this), diff --git a/packages/neo4j-driver-deno/lib/core/transaction-promise.ts b/packages/neo4j-driver-deno/lib/core/transaction-promise.ts index 157588735..fe4b5c19f 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction-promise.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction-promise.ts @@ -65,7 +65,8 @@ class TransactionPromise extends Transaction implements Promise { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }: { connectionHolder: ConnectionHolder onClose: () => void @@ -76,6 +77,7 @@ class TransactionPromise extends Transaction implements Promise { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number + notificationFilters?: string[] }) { super({ connectionHolder, @@ -86,7 +88,8 @@ class TransactionPromise extends Transaction implements Promise { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }) } diff --git a/packages/neo4j-driver-deno/lib/core/transaction.ts b/packages/neo4j-driver-deno/lib/core/transaction.ts index 5e4b98b96..caa7ce20d 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction.ts @@ -61,6 +61,7 @@ class Transaction { private _bookmarks: Bookmarks private readonly _activePromise: Promise private _acceptActive: () => void + private readonly _notificationFilters?: string[] /** * @constructor @@ -84,7 +85,8 @@ class Transaction { fetchSize, impersonatedUser, highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }: { connectionHolder: ConnectionHolder onClose: () => void @@ -95,6 +97,7 @@ class Transaction { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number + notificationFilters?: string[] }) { this._connectionHolder = connectionHolder this._reactive = reactive @@ -110,6 +113,7 @@ class Transaction { this._lowRecordWatermak = lowRecordWatermark this._highRecordWatermark = highRecordWatermark this._bookmarks = Bookmarks.empty() + this._notificationFilters = notificationFilters this._acceptActive = () => { } // satisfy DenoJS this._activePromise = new Promise((resolve, reject) => { this._acceptActive = resolve @@ -138,6 +142,7 @@ class Transaction { mode: this._connectionHolder.mode(), database: this._connectionHolder.database(), impersonatedUser: this._impersonatedUser, + notificationFilters: this._notificationFilters, beforeError: (error: Error) => { if (events != null) { events.onError(error) diff --git a/packages/neo4j-driver/src/driver.js b/packages/neo4j-driver/src/driver.js index 491c43f02..1c73be7d1 100644 --- a/packages/neo4j-driver/src/driver.js +++ b/packages/neo4j-driver/src/driver.js @@ -58,7 +58,8 @@ class Driver extends CoreDriver { database = '', fetchSize, impersonatedUser, - bookmarkManager + bookmarkManager, + notificationFilters } = {}) { return new RxSession({ session: this._newSession({ @@ -68,7 +69,8 @@ class Driver extends CoreDriver { impersonatedUser, reactive: false, fetchSize: validateFetchSizeValue(fetchSize, this._config.fetchSize), - bookmarkManager + bookmarkManager, + notificationFilters }), config: this._config }) From 0d255e73f47bdb93c438cb05a89e8c0e9e0e2eaa Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 27 Oct 2022 18:25:11 +0200 Subject: [PATCH 09/27] testkit --- .../connection-provider-pooled.js | 4 ++-- .../connection-provider-pooled.js | 4 ++-- .../testkit-backend/src/feature/common.js | 5 +++-- .../src/request-handlers-rx.js | 14 ++++++++++++-- .../testkit-backend/src/request-handlers.js | 19 +++++++++++++++++-- 5 files changed, 36 insertions(+), 10 deletions(-) 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 0c5067013..60ae3cbd4 100644 --- a/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js +++ b/packages/bolt-connection/src/connection-provider/connection-provider-pooled.js @@ -24,7 +24,7 @@ import { error, ConnectionProvider, ServerInfo } from 'neo4j-driver-core' const { SERVICE_UNAVAILABLE } = error export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authToken, notificationFilters }, + { id, config, log, userAgent, authToken }, createChannelConnectionHook = null ) { super() @@ -34,7 +34,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._log = log this._userAgent = userAgent this._authToken = authToken - this._notificationFilters = notificationFilters + this._notificationFilters = this._config.notificationFilters this._createChannelConnection = createChannelConnectionHook || (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 67c53f594..5d72ee881 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 @@ -24,7 +24,7 @@ import { error, ConnectionProvider, ServerInfo } from '../../core/index.ts' const { SERVICE_UNAVAILABLE } = error export default class PooledConnectionProvider extends ConnectionProvider { constructor ( - { id, config, log, userAgent, authToken, notificationFilters }, + { id, config, log, userAgent, authToken }, createChannelConnectionHook = null ) { super() @@ -34,7 +34,7 @@ export default class PooledConnectionProvider extends ConnectionProvider { this._log = log this._userAgent = userAgent this._authToken = authToken - this._notificationFilters = notificationFilters + this._notificationFilters = this._config.notificationFilters this._createChannelConnection = createChannelConnectionHook || (address => { diff --git a/packages/testkit-backend/src/feature/common.js b/packages/testkit-backend/src/feature/common.js index e7c8c5a2a..5eb03ceff 100644 --- a/packages/testkit-backend/src/feature/common.js +++ b/packages/testkit-backend/src/feature/common.js @@ -1,6 +1,4 @@ - - const features = [ 'Feature:Auth:Custom', 'Feature:Auth:Kerberos', @@ -18,10 +16,13 @@ const features = [ 'Feature:Bolt:4.3', 'Feature:Bolt:4.4', 'Feature:Bolt:5.0', + 'Feature:Bolt:5.1', 'Feature:Bolt:Patch:UTC', 'Feature:API:ConnectionAcquisitionTimeout', 'Feature:API:Driver:GetServerInfo', + 'Feature:API:Driver.NotificationFilters', 'Feature:API:Driver.VerifyConnectivity', + 'Feature:API:Session.NotificationFilters', 'Optimization:EagerTransactionBegin', 'Optimization:ImplicitDefaultArguments', 'Optimization:MinimalBookmarksSet', diff --git a/packages/testkit-backend/src/request-handlers-rx.js b/packages/testkit-backend/src/request-handlers-rx.js index 8a882dfa4..6fa3939a8 100644 --- a/packages/testkit-backend/src/request-handlers-rx.js +++ b/packages/testkit-backend/src/request-handlers-rx.js @@ -25,7 +25,16 @@ export { } from './request-handlers.js' export function NewSession (neo4j, context, data, wire) { - let { driverId, accessMode, bookmarks, database, fetchSize, impersonatedUser, bookmarkManagerId } = data + let { + driverId, + accessMode, + bookmarks, + database, + fetchSize, + impersonatedUser, + bookmarkManagerId, + notificationFilters + } = data switch (accessMode) { case 'r': accessMode = neo4j.session.READ @@ -52,7 +61,8 @@ export function NewSession (neo4j, context, data, wire) { database, fetchSize, impersonatedUser, - bookmarkManager + bookmarkManager, + notificationFilters }) const id = context.addSession(session) wire.writeResponse(responses.Session({ id })) diff --git a/packages/testkit-backend/src/request-handlers.js b/packages/testkit-backend/src/request-handlers.js index 49b8fb0a2..d628e6017 100644 --- a/packages/testkit-backend/src/request-handlers.js +++ b/packages/testkit-backend/src/request-handlers.js @@ -83,6 +83,10 @@ export function NewDriver (neo4j, context, data, wire) { if ('maxTxRetryTimeMs' in data) { config.maxTransactionRetryTime = data.maxTxRetryTimeMs } + if ('notificationFilters' in data) { + config.notificationFilters = data.notificationFilters + } + let driver try { driver = neo4j.driver(uri, parsedAuthToken, config) @@ -106,7 +110,17 @@ export function DriverClose (_, context, data, wire) { } export function NewSession (neo4j, context, data, wire) { - let { driverId, accessMode, bookmarks, database, fetchSize, impersonatedUser, bookmarkManagerId } = data + let { + driverId, + accessMode, + bookmarks, + database, + fetchSize, + impersonatedUser, + bookmarkManagerId, + notificationFilters + } = data + switch (accessMode) { case 'r': accessMode = neo4j.session.READ @@ -133,7 +147,8 @@ export function NewSession (neo4j, context, data, wire) { database, fetchSize, impersonatedUser, - bookmarkManager + bookmarkManager, + notificationFilters }) const id = context.addSession(session) wire.writeResponse(responses.Session({ id })) From 8b6dcf3dd19602c613118dadf60443ac9827a4f3 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 1 Nov 2022 13:38:21 +0100 Subject: [PATCH 10/27] Adjust categories --- packages/core/src/result-summary.ts | 9 +++++---- packages/core/test/result-summary.test.ts | 3 ++- packages/neo4j-driver-deno/lib/core/result-summary.ts | 9 +++++---- packages/neo4j-driver-lite/test/unit/index.test.ts | 3 ++- packages/neo4j-driver/test/types/index.test.ts | 6 ++++-- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index ebbf8fff9..9f328a461 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -435,10 +435,10 @@ const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { Object.freeze(notificationSeverityLevel) const severityLevels = Object.values(notificationSeverityLevel) -type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | -'DEPRECATION' | 'RUNTIME' | 'UNKNOWN' +type NotificationCategory = 'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | +'DEPRECATION' | 'GENERIC' | 'UNKNOWN' /** - * @typedef {'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + * @typedef {'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory */ /** * Constants that represents the Category in the {@link Notification} @@ -446,10 +446,11 @@ type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | const notificationCategory: { [key in NotificationCategory]: key } = { HINT: 'HINT', QUERY: 'QUERY', + UNRECOGNIZED: 'UNRECOGNIZED', UNSUPPORTED: 'UNSUPPORTED', PERFORMANCE: 'PERFORMANCE', DEPRECATION: 'DEPRECATION', - RUNTIME: 'RUNTIME', + GENERIC: 'GENERIC', UNKNOWN: 'UNKNOWN' } diff --git a/packages/core/test/result-summary.test.ts b/packages/core/test/result-summary.test.ts index 7aca88e14..15d3d7ae3 100644 --- a/packages/core/test/result-summary.test.ts +++ b/packages/core/test/result-summary.test.ts @@ -170,10 +170,11 @@ function getValidCategories (): NotificationCategory[] { return [ 'HINT', 'QUERY', + 'UNRECOGNIZED', 'UNSUPPORTED', 'PERFORMANCE', 'DEPRECATION', - 'RUNTIME', + 'GENERIC', 'UNKNOWN' ] } diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index c0e2a8557..7808d6e8b 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -435,10 +435,10 @@ const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { Object.freeze(notificationSeverityLevel) const severityLevels = Object.values(notificationSeverityLevel) -type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | -'DEPRECATION' | 'RUNTIME' | 'UNKNOWN' +type NotificationCategory = 'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | +'DEPRECATION' | 'GENERIC' | 'UNKNOWN' /** - * @typedef {'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + * @typedef {'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory */ /** * Constants that represents the Category in the {@link Notification} @@ -446,10 +446,11 @@ type NotificationCategory = 'HINT' | 'QUERY' | 'UNSUPPORTED' |'PERFORMANCE' | const notificationCategory: { [key in NotificationCategory]: key } = { HINT: 'HINT', QUERY: 'QUERY', + UNRECOGNIZED: 'UNRECOGNIZED', UNSUPPORTED: 'UNSUPPORTED', PERFORMANCE: 'PERFORMANCE', DEPRECATION: 'DEPRECATION', - RUNTIME: 'RUNTIME', + GENERIC: 'GENERIC', UNKNOWN: 'UNKNOWN' } diff --git a/packages/neo4j-driver-lite/test/unit/index.test.ts b/packages/neo4j-driver-lite/test/unit/index.test.ts index b7e9945c1..9b47ef823 100644 --- a/packages/neo4j-driver-lite/test/unit/index.test.ts +++ b/packages/neo4j-driver-lite/test/unit/index.test.ts @@ -319,10 +319,11 @@ describe('index', () => { expect(neo4j.notificationCategory).toBeDefined() expect(neo4j.notificationCategory.HINT).toBeDefined() expect(neo4j.notificationCategory.QUERY).toBeDefined() + expect(neo4j.notificationCategory.UNRECOGNIZED).toBeDefined() expect(neo4j.notificationCategory.UNSUPPORTED).toBeDefined() expect(neo4j.notificationCategory.PERFORMANCE).toBeDefined() expect(neo4j.notificationCategory.DEPRECATION).toBeDefined() - expect(neo4j.notificationCategory.RUNTIME).toBeDefined() + expect(neo4j.notificationCategory.GENERIC).toBeDefined() expect(neo4j.notificationCategory.UNKNOWN).toBeDefined() }) }) diff --git a/packages/neo4j-driver/test/types/index.test.ts b/packages/neo4j-driver/test/types/index.test.ts index cb6cb734b..81b0d4dae 100644 --- a/packages/neo4j-driver/test/types/index.test.ts +++ b/packages/neo4j-driver/test/types/index.test.ts @@ -102,13 +102,15 @@ const hintCategoryString: string = notificationCategory.HINT const deprecationCategoryString: string = notificationCategory.DEPRECATION const performanceCategoryString: string = notificationCategory.PERFORMANCE const queryCategoryString: string = notificationCategory.QUERY -const runtimeCategoryString: string = notificationCategory.RUNTIME +const genericCategoryString: string = notificationCategory.GENERIC +const unrecognizedCategoryString: string = notificationCategory.UNRECOGNIZED const unsupportedCategoryString: string = notificationCategory.UNSUPPORTED const unknownCategoryString: string = notificationCategory.UNKNOWN const hintCategory: NotificationCategory = notificationCategory.HINT const deprecationCategory: NotificationCategory = notificationCategory.DEPRECATION const performanceCategory: NotificationCategory = notificationCategory.PERFORMANCE const queryCategory: NotificationCategory = notificationCategory.QUERY -const runtimeCategory: NotificationCategory = notificationCategory.RUNTIME +const genericCategory: NotificationCategory = notificationCategory.GENERIC +const unrecognizedCategory: NotificationCategory = notificationCategory.UNRECOGNIZED const unsupportedCategory: NotificationCategory = notificationCategory.UNSUPPORTED const unknownCategory: NotificationCategory = notificationCategory.UNKNOWN From 49d070b8b70bd81d6676aafacf3b658f8df8b7be Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 1 Nov 2022 15:02:13 +0100 Subject: [PATCH 11/27] Update HELLO message for 5.1 --- .../src/bolt/bolt-protocol-v5x1.js | 6 +++- .../src/bolt/request-message.js | 31 ++++++++++++++++--- .../test/bolt/bolt-protocol-v5x1.test.js | 4 +-- .../test/bolt/request-message.test.js | 31 +++++++++++++++---- .../bolt/bolt-protocol-v5x1.js | 6 +++- .../bolt-connection/bolt/request-message.js | 31 ++++++++++++++++--- 6 files changed, 89 insertions(+), 20 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index a0c7586fd..3dcbe89e1 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -59,7 +59,11 @@ export default class BoltProtocol extends BoltProtocolV5x0 { }) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting, null, { notificationFilters }), + RequestMessage.hello5x1(authToken, { + userAgent, + notificationFilters, + routing: this._serversideRouting + }), observer, true ) diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index f95ddb5b5..8099b4cd5 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -105,10 +105,9 @@ export default class RequestMessage { * @param {Object} authToken the authentication token. * @param {Object} optional server side routing, set to routing context to turn on server side routing (> 4.1) * @param {?string[]} patchs patches to be applied to the server (valid in 4.3 and 4.4) - * @param {?string[]} notificationFilters the cypher notification filters * @return {RequestMessage} new HELLO message. */ - static hello (userAgent, authToken, routing = null, patchs = null, { notificationFilters } = {}) { + static hello (userAgent, authToken, routing = null, patchs = null) { const metadata = Object.assign({ user_agent: userAgent }, authToken) if (routing) { metadata.routing = routing @@ -116,9 +115,6 @@ export default class RequestMessage { if (patchs) { metadata.patch_bolt = patchs } - if (notificationFilters) { - metadata.notifications = notificationFilters - } return new RequestMessage( HELLO, [metadata], @@ -126,6 +122,31 @@ export default class RequestMessage { ) } + /** + * Create a new HELLO message. + * @param {Object} authToken the authentication token. + * @param {Object} param1 the extra information + * @param {string} param1.userAgent the user agent + * @param {?Object} param1.routing the server side routing context, when set the server side routing is enabled + * @param {?string[]} param1.notificationFilters the cypher notification filters + */ + static hello5x1 (authToken, { userAgent, routing, notificationFilters } = {}) { + const extra = { user_agent: userAgent } + if (routing) { + extra.routing = routing + } + + if (notificationFilters) { + extra.notifications = notificationFilters + } + + return new RequestMessage( + HELLO, + [authToken, extra], + () => `HELLO {...} ${json.stringify(extra)}` + ) + } + /** * Create a new BEGIN message. * @param {Bookmarks} bookmarks the bookmarks. 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 1c50b129f..f5f6a1e1a 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -346,7 +346,7 @@ describe('#unit BoltProtocolV5x1', () => { protocol.verifyMessageCount(1) expect(protocol.messages[0]).toBeMessage( - RequestMessage.hello(clientName, authToken) + RequestMessage.hello5x1(authToken, { userAgent: clientName }) ) expect(protocol.observers).toEqual([observer]) expect(protocol.flushes).toEqual([true]) @@ -365,7 +365,7 @@ describe('#unit BoltProtocolV5x1', () => { protocol.verifyMessageCount(1) expect(protocol.messages[0]).toBeMessage( - RequestMessage.hello(clientName, authToken, false, null, { notificationFilters }) + RequestMessage.hello5x1(authToken, { routing: false, userAgent: clientName, notificationFilters }) ) expect(protocol.observers).toEqual([observer]) expect(protocol.flushes).toEqual([true]) diff --git a/packages/bolt-connection/test/bolt/request-message.test.js b/packages/bolt-connection/test/bolt/request-message.test.js index c865a6b22..7f6c35917 100644 --- a/packages/bolt-connection/test/bolt/request-message.test.js +++ b/packages/bolt-connection/test/bolt/request-message.test.js @@ -436,14 +436,15 @@ describe('#unit RequestMessage', () => { const authToken = { username: 'neo4j', password: 'secret' } const notificationFilters = ['*.*', 'WARNING.*'] - const message = RequestMessage.hello(userAgent, authToken, null, null, { notificationFilters }) + const message = RequestMessage.hello5x1(authToken, { userAgent, notificationFilters }) expect(message.signature).toEqual(0x01) expect(message.fields).toEqual([ - { user_agent: userAgent, username: 'neo4j', password: 'secret', notifications: notificationFilters } + { username: 'neo4j', password: 'secret' }, + { user_agent: userAgent, notifications: notificationFilters } ]) expect(message.toString()).toEqual( - `HELLO {user_agent: '${userAgent}', ...}` + `HELLO {...} {"user_agent":"${userAgent}","notifications":["*.*","WARNING.*"]}` ) }) @@ -452,18 +453,36 @@ describe('#unit RequestMessage', () => { const userAgent = 'my-driver/1.0.2' const authToken = { username: 'neo4j', password: 'secret' } - const message = RequestMessage.hello(userAgent, authToken, null, null, { notificationFilters }) + const message = RequestMessage.hello5x1(authToken, { userAgent, notificationFilters }) expect(message.signature).toEqual(0x01) expect(message.fields).toEqual([ - { user_agent: userAgent, username: 'neo4j', password: 'secret' } + { username: 'neo4j', password: 'secret' }, + { user_agent: userAgent } ]) expect(message.toString()).toEqual( - `HELLO {user_agent: '${userAgent}', ...}` + `HELLO {...} {"user_agent":"${userAgent}"}` ) }) }) + it('should create HELLO message with routing', () => { + const userAgent = 'my-driver/1.0.2' + const authToken = { username: 'neo4j', password: 'secret' } + const routing = { address: 'localhost123:9090', otherInfo: 'info' } + + const message = RequestMessage.hello5x1(authToken, { userAgent, routing }) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + { username: 'neo4j', password: 'secret' }, + { user_agent: userAgent, routing } + ]) + expect(message.toString()).toEqual( + `HELLO {...} {"user_agent":"${userAgent}","routing":{"address":"localhost123:9090","otherInfo":"info"}}` + ) + }) + it('should create BEGIN message with notification filters', () => { ;[READ, WRITE].forEach(mode => { const bookmarks = new Bookmarks([ 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 36a3d14f6..76d9e4583 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 @@ -59,7 +59,11 @@ export default class BoltProtocol extends BoltProtocolV5x0 { }) this.write( - RequestMessage.hello(userAgent, authToken, this._serversideRouting, null, { notificationFilters }), + RequestMessage.hello5x1(authToken, { + userAgent, + notificationFilters, + routing: this._serversideRouting + }), observer, true ) 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 8dde1b7e9..2c2024139 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 @@ -105,10 +105,9 @@ export default class RequestMessage { * @param {Object} authToken the authentication token. * @param {Object} optional server side routing, set to routing context to turn on server side routing (> 4.1) * @param {?string[]} patchs patches to be applied to the server (valid in 4.3 and 4.4) - * @param {?string[]} notificationFilters the cypher notification filters * @return {RequestMessage} new HELLO message. */ - static hello (userAgent, authToken, routing = null, patchs = null, { notificationFilters } = {}) { + static hello (userAgent, authToken, routing = null, patchs = null) { const metadata = Object.assign({ user_agent: userAgent }, authToken) if (routing) { metadata.routing = routing @@ -116,9 +115,6 @@ export default class RequestMessage { if (patchs) { metadata.patch_bolt = patchs } - if (notificationFilters) { - metadata.notifications = notificationFilters - } return new RequestMessage( HELLO, [metadata], @@ -126,6 +122,31 @@ export default class RequestMessage { ) } + /** + * Create a new HELLO message. + * @param {Object} authToken the authentication token. + * @param {Object} param1 the extra information + * @param {string} param1.userAgent the user agent + * @param {?Object} param1.routing the server side routing context, when set the server side routing is enabled + * @param {?string[]} param1.notificationFilters the cypher notification filters + */ + static hello5x1 (authToken, { userAgent, routing, notificationFilters } = {}) { + const extra = { user_agent: userAgent } + if (routing) { + extra.routing = routing + } + + if (notificationFilters) { + extra.notifications = notificationFilters + } + + return new RequestMessage( + HELLO, + [authToken, extra], + () => `HELLO {...} ${json.stringify(extra)}` + ) + } + /** * Create a new BEGIN message. * @param {Bookmarks} bookmarks the bookmarks. From 759498875f5fa4a1980d84b55324bd0aa9a8ec73 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 1 Nov 2022 15:05:44 +0100 Subject: [PATCH 12/27] Small adjustment --- packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js | 2 +- .../lib/bolt-connection/bolt/bolt-protocol-v5x1.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index 3dcbe89e1..f8bc685a1 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -48,7 +48,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { * @param {string} param0.userAgent The user agent * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback - * @param {function(onComplte)} param0.onComplete On complete callback + * @param {function(onComplete)} param0.onComplete On complete callback * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ 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 76d9e4583..d2d66c255 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 @@ -48,7 +48,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { * @param {string} param0.userAgent The user agent * @param {any} param0.authToken The auth token * @param {function(error)} param0.onError On error callback - * @param {function(onComplte)} param0.onComplete On complete callback + * @param {function(onComplete)} param0.onComplete On complete callback * @param {?string[]} param0.notificationFilters the filtering for notifications. * @returns {LoginObserver} The Login observer */ From 73b86af49268f1da497b211177d98dea7ca3d440 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 1 Nov 2022 17:01:05 +0100 Subject: [PATCH 13/27] Fix features name --- packages/testkit-backend/src/feature/common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/testkit-backend/src/feature/common.js b/packages/testkit-backend/src/feature/common.js index 5eb03ceff..c0d200fc7 100644 --- a/packages/testkit-backend/src/feature/common.js +++ b/packages/testkit-backend/src/feature/common.js @@ -20,9 +20,9 @@ const features = [ 'Feature:Bolt:Patch:UTC', 'Feature:API:ConnectionAcquisitionTimeout', 'Feature:API:Driver:GetServerInfo', - 'Feature:API:Driver.NotificationFilters', + 'Feature:API:Driver:NotificationFilters', 'Feature:API:Driver.VerifyConnectivity', - 'Feature:API:Session.NotificationFilters', + 'Feature:API:Session:NotificationFilters', 'Optimization:EagerTransactionBegin', 'Optimization:ImplicitDefaultArguments', 'Optimization:MinimalBookmarksSet', From 03d610962190121febfb034124d38d6ee5ec566a Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 13:42:31 +0100 Subject: [PATCH 14/27] Add NotificationFilter and notificationFilter to core --- packages/core/src/driver.ts | 10 +- packages/core/src/notification-filter.ts | 98 +++++++++++++++++++ packages/core/src/result-summary.ts | 4 +- packages/core/src/session.ts | 5 +- packages/core/src/transaction-promise.ts | 3 +- packages/core/src/transaction.ts | 5 +- packages/neo4j-driver-deno/lib/core/driver.ts | 10 +- .../lib/core/notification-filter.ts | 98 +++++++++++++++++++ .../lib/core/result-summary.ts | 4 +- .../neo4j-driver-deno/lib/core/session.ts | 5 +- .../lib/core/transaction-promise.ts | 3 +- .../neo4j-driver-deno/lib/core/transaction.ts | 5 +- 12 files changed, 230 insertions(+), 20 deletions(-) create mode 100644 packages/core/src/notification-filter.ts create mode 100644 packages/neo4j-driver-deno/lib/core/notification-filter.ts diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 3b8082f79..65fb6d393 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -20,6 +20,7 @@ import ConnectionProvider from './connection-provider' import { Bookmarks } from './internal/bookmarks' import ConfiguredCustomResolver from './internal/resolver/configured-custom-resolver' +import { NotificationFilter } from './notification-filter' import { ACCESS_MODE_READ, @@ -89,7 +90,7 @@ type CreateSession = (args: { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) => Session interface DriverConfig { @@ -97,7 +98,7 @@ interface DriverConfig { trust?: TrustStrategy fetchSize?: number logging?: LoggingConfig - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] } /** @@ -112,7 +113,7 @@ class SessionConfig { impersonatedUser?: string fetchSize?: number bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] /** * @constructor @@ -196,6 +197,7 @@ class SessionConfig { /** * @todo docs + * @type {NotificationFilter[]|undefined} */ this.notificationFilters = undefined } @@ -455,7 +457,7 @@ class Driver { impersonatedUser?: string fetchSize: number bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }): Session { const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts new file mode 100644 index 000000000..da9c2572f --- /dev/null +++ b/packages/core/src/notification-filter.ts @@ -0,0 +1,98 @@ +/** + * 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 { NotificationCategory, NotificationSeverityLevel } from './result-summary' + +type ExcludeUnknown = Exclude + +type FilterKeys = ExcludeUnknown | 'ALL' +type FilterInnerKeys = ExcludeUnknown | 'ALL' + +type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } +interface AuxFunctions { + disabled: () => NotificationFilter[] + serverDefault: () => NotificationFilter[] +} + +type NotificationFilter = + 'NONE' | 'SERVER_DEFAULT' | + 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | + 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | + 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | + 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' + +/** + * @typedef { 'NONE' | 'SERVER_DEFAULT' | + * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | + * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + * 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | + * 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | + * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter + */ + +/** + * Constants that represents the available notification filters + * + * @property {function(): Array} disabled Creates a configuration with notifications disabled + * @property {function(): Array} serverDefault Creates a configuration for using the server default + */ +const notificationFilter: SeverityDotCategoryFilters & AuxFunctions = { + disabled: () => ['NONE'], + serverDefault: () => ['SERVER_DEFAULT'], + ALL: { + ALL: 'ALL.ALL', + DEPRECATION: 'ALL.DEPRECATION', + GENERIC: 'ALL.GENERIC', + HINT: 'ALL.HINT', + PERFORMANCE: 'ALL.PERFORMANCE', + QUERY: 'ALL.QUERY', + UNRECOGNIZED: 'ALL.UNRECOGNIZED', + UNSUPPORTED: 'ALL.UNSUPPORTED' + }, + INFORMATION: { + ALL: 'INFORMATION.ALL', + DEPRECATION: 'INFORMATION.DEPRECATION', + GENERIC: 'INFORMATION.GENERIC', + HINT: 'INFORMATION.HINT', + PERFORMANCE: 'INFORMATION.PERFORMANCE', + QUERY: 'INFORMATION.QUERY', + UNRECOGNIZED: 'INFORMATION.UNRECOGNIZED', + UNSUPPORTED: 'INFORMATION.UNSUPPORTED' + }, + WARNING: { + ALL: 'WARNING.ALL', + DEPRECATION: 'WARNING.DEPRECATION', + GENERIC: 'WARNING.GENERIC', + HINT: 'WARNING.HINT', + PERFORMANCE: 'WARNING.PERFORMANCE', + QUERY: 'WARNING.QUERY', + UNRECOGNIZED: 'WARNING.UNRECOGNIZED', + UNSUPPORTED: 'WARNING.UNSUPPORTED' + } +} + +Object.freeze(notificationFilter) + +export default notificationFilter + +export type { + NotificationFilter +} diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index 9f328a461..aa620331f 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -498,10 +498,12 @@ class Notification { this.description = notification.description /** * The raw severity + * + * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} enumerated value. + * * @type {string} * @public * @deprecated This property will be removed in 6.0. - * Use {@link Notification.rawSeverityLevel} for the raw value or {@link Notification.severityLevel} enumerated value. */ this.severity = notification.severity /** diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts index b3927c1c6..e3b245f34 100644 --- a/packages/core/src/session.ts +++ b/packages/core/src/session.ts @@ -37,6 +37,7 @@ import TransactionPromise from './transaction-promise' import ManagedTransaction from './transaction-managed' import BookmarkManager from './bookmark-manager' import { Dict } from './record' +import { NotificationFilter } from './notification-filter' type ConnectionConsumer = (connection: Connection | null) => any | undefined | Promise | Promise type TransactionWork = (tx: Transaction) => Promise | T @@ -72,7 +73,7 @@ class Session { private readonly _highRecordWatermark: number private readonly _results: Result[] private readonly _bookmarkManager?: BookmarkManager - private readonly _notificationFilters?: string[] + private readonly _notificationFilters?: NotificationFilter[] /** * @constructor * @protected @@ -107,7 +108,7 @@ class Session { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { this._mode = mode this._database = database diff --git a/packages/core/src/transaction-promise.ts b/packages/core/src/transaction-promise.ts index 2ace08797..86c550876 100644 --- a/packages/core/src/transaction-promise.ts +++ b/packages/core/src/transaction-promise.ts @@ -26,6 +26,7 @@ import { import { Bookmarks } from './internal/bookmarks' import { TxConfig } from './internal/tx-config' +import { NotificationFilter } from './notification-filter' /** * Represents a {@link Promise} object and a {@link Transaction} object. @@ -77,7 +78,7 @@ class TransactionPromise extends Transaction implements Promise { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { super({ connectionHolder, diff --git a/packages/core/src/transaction.ts b/packages/core/src/transaction.ts index 2750f91a8..bf413107c 100644 --- a/packages/core/src/transaction.ts +++ b/packages/core/src/transaction.ts @@ -38,6 +38,7 @@ import { newError } from './error' import Result from './result' import { Query } from './types' import { Dict } from './record' +import { NotificationFilter } from './notification-filter' /** * Represents a transaction in the Neo4j database. @@ -61,7 +62,7 @@ class Transaction { private _bookmarks: Bookmarks private readonly _activePromise: Promise private _acceptActive: () => void - private readonly _notificationFilters?: string[] + private readonly _notificationFilters?: NotificationFilter[] /** * @constructor @@ -97,7 +98,7 @@ class Transaction { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { this._connectionHolder = connectionHolder this._reactive = reactive diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 809baa221..e96dc2fb2 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -20,6 +20,7 @@ import ConnectionProvider from './connection-provider.ts' import { Bookmarks } from './internal/bookmarks.ts' import ConfiguredCustomResolver from './internal/resolver/configured-custom-resolver.ts' +import { NotificationFilter } from './notification-filter.ts' import { ACCESS_MODE_READ, @@ -89,7 +90,7 @@ type CreateSession = (args: { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) => Session interface DriverConfig { @@ -97,7 +98,7 @@ interface DriverConfig { trust?: TrustStrategy fetchSize?: number logging?: LoggingConfig - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] } /** @@ -112,7 +113,7 @@ class SessionConfig { impersonatedUser?: string fetchSize?: number bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] /** * @constructor @@ -196,6 +197,7 @@ class SessionConfig { /** * @todo docs + * @type {NotificationFilter[]|undefined} */ this.notificationFilters = undefined } @@ -455,7 +457,7 @@ class Driver { impersonatedUser?: string fetchSize: number bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }): Session { const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts new file mode 100644 index 000000000..bdedcf8d8 --- /dev/null +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -0,0 +1,98 @@ +/** + * 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 { NotificationCategory, NotificationSeverityLevel } from './result-summary.ts' + +type ExcludeUnknown = Exclude + +type FilterKeys = ExcludeUnknown | 'ALL' +type FilterInnerKeys = ExcludeUnknown | 'ALL' + +type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } +interface AuxFunctions { + disabled: () => NotificationFilter[] + serverDefault: () => NotificationFilter[] +} + +type NotificationFilter = + 'NONE' | 'SERVER_DEFAULT' | + 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | + 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | + 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | + 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' + +/** + * @typedef { 'NONE' | 'SERVER_DEFAULT' | + * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | + * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + * 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | + * 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | + * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter + */ + +/** + * Constants that represents the available notification filters + * + * @property {function(): Array} disabled Creates a configuration with notifications disabled + * @property {function(): Array} serverDefault Creates a configuration for using the server default + */ +const notificationFilter: SeverityDotCategoryFilters & AuxFunctions = { + disabled: () => ['NONE'], + serverDefault: () => ['SERVER_DEFAULT'], + ALL: { + ALL: 'ALL.ALL', + DEPRECATION: 'ALL.DEPRECATION', + GENERIC: 'ALL.GENERIC', + HINT: 'ALL.HINT', + PERFORMANCE: 'ALL.PERFORMANCE', + QUERY: 'ALL.QUERY', + UNRECOGNIZED: 'ALL.UNRECOGNIZED', + UNSUPPORTED: 'ALL.UNSUPPORTED' + }, + INFORMATION: { + ALL: 'INFORMATION.ALL', + DEPRECATION: 'INFORMATION.DEPRECATION', + GENERIC: 'INFORMATION.GENERIC', + HINT: 'INFORMATION.HINT', + PERFORMANCE: 'INFORMATION.PERFORMANCE', + QUERY: 'INFORMATION.QUERY', + UNRECOGNIZED: 'INFORMATION.UNRECOGNIZED', + UNSUPPORTED: 'INFORMATION.UNSUPPORTED' + }, + WARNING: { + ALL: 'WARNING.ALL', + DEPRECATION: 'WARNING.DEPRECATION', + GENERIC: 'WARNING.GENERIC', + HINT: 'WARNING.HINT', + PERFORMANCE: 'WARNING.PERFORMANCE', + QUERY: 'WARNING.QUERY', + UNRECOGNIZED: 'WARNING.UNRECOGNIZED', + UNSUPPORTED: 'WARNING.UNSUPPORTED' + } +} + +Object.freeze(notificationFilter) + +export default notificationFilter + +export type { + NotificationFilter +} diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index 7808d6e8b..8780a44c1 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -498,10 +498,12 @@ class Notification { this.description = notification.description /** * The raw severity + * + * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} enumerated value. + * * @type {string} * @public * @deprecated This property will be removed in 6.0. - * Use {@link Notification.rawSeverityLevel} for the raw value or {@link Notification.severityLevel} enumerated value. */ this.severity = notification.severity /** diff --git a/packages/neo4j-driver-deno/lib/core/session.ts b/packages/neo4j-driver-deno/lib/core/session.ts index f4ae2d5c5..82b4a6adb 100644 --- a/packages/neo4j-driver-deno/lib/core/session.ts +++ b/packages/neo4j-driver-deno/lib/core/session.ts @@ -37,6 +37,7 @@ import TransactionPromise from './transaction-promise.ts' import ManagedTransaction from './transaction-managed.ts' import BookmarkManager from './bookmark-manager.ts' import { Dict } from './record.ts' +import { NotificationFilter } from './notification-filter.ts' type ConnectionConsumer = (connection: Connection | null) => any | undefined | Promise | Promise type TransactionWork = (tx: Transaction) => Promise | T @@ -72,7 +73,7 @@ class Session { private readonly _highRecordWatermark: number private readonly _results: Result[] private readonly _bookmarkManager?: BookmarkManager - private readonly _notificationFilters?: string[] + private readonly _notificationFilters?: NotificationFilter[] /** * @constructor * @protected @@ -107,7 +108,7 @@ class Session { fetchSize: number impersonatedUser?: string bookmarkManager?: BookmarkManager - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { this._mode = mode this._database = database diff --git a/packages/neo4j-driver-deno/lib/core/transaction-promise.ts b/packages/neo4j-driver-deno/lib/core/transaction-promise.ts index fe4b5c19f..8cd54285d 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction-promise.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction-promise.ts @@ -26,6 +26,7 @@ import { import { Bookmarks } from './internal/bookmarks.ts' import { TxConfig } from './internal/tx-config.ts' +import { NotificationFilter } from './notification-filter.ts' /** * Represents a {@link Promise} object and a {@link Transaction} object. @@ -77,7 +78,7 @@ class TransactionPromise extends Transaction implements Promise { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { super({ connectionHolder, diff --git a/packages/neo4j-driver-deno/lib/core/transaction.ts b/packages/neo4j-driver-deno/lib/core/transaction.ts index caa7ce20d..e09b86c88 100644 --- a/packages/neo4j-driver-deno/lib/core/transaction.ts +++ b/packages/neo4j-driver-deno/lib/core/transaction.ts @@ -38,6 +38,7 @@ import { newError } from './error.ts' import Result from './result.ts' import { Query } from './types.ts' import { Dict } from './record.ts' +import { NotificationFilter } from './notification-filter.ts' /** * Represents a transaction in the Neo4j database. @@ -61,7 +62,7 @@ class Transaction { private _bookmarks: Bookmarks private readonly _activePromise: Promise private _acceptActive: () => void - private readonly _notificationFilters?: string[] + private readonly _notificationFilters?: NotificationFilter[] /** * @constructor @@ -97,7 +98,7 @@ class Transaction { impersonatedUser?: string highRecordWatermark: number lowRecordWatermark: number - notificationFilters?: string[] + notificationFilters?: NotificationFilter[] }) { this._connectionHolder = connectionHolder this._reactive = reactive From 24bbe3f3b678be6c042c94112fba63a911aca08d Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 15:21:20 +0100 Subject: [PATCH 15/27] Export NotificationFilter and notificationFilter --- packages/core/src/index.ts | 10 +++- packages/core/src/notification-filter.ts | 9 ++- packages/neo4j-driver-deno/lib/core/index.ts | 10 +++- .../lib/core/notification-filter.ts | 9 ++- packages/neo4j-driver-deno/lib/mod.ts | 13 +++-- packages/neo4j-driver-lite/src/index.ts | 13 +++-- .../neo4j-driver-lite/test/unit/index.test.ts | 9 +++ packages/neo4j-driver/src/index.js | 9 ++- .../neo4j-driver/test/types/index.test.ts | 58 ++++++++++++++++++- packages/neo4j-driver/types/index.d.ts | 11 +++- 10 files changed, 120 insertions(+), 31 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4a7d893d8..9f5464823 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -70,6 +70,7 @@ import ResultSummary, { notificationCategory, notificationSeverityLevel } from './result-summary' +import notificationFilter, { NotificationFilter } from './notification-filter' import Result, { QueryResult, ResultObserver } from './result' import ConnectionProvider from './connection-provider' import Connection from './connection' @@ -155,7 +156,8 @@ const forExport = { auth, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export { @@ -217,7 +219,8 @@ export { auth, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export type { @@ -231,7 +234,8 @@ export type { BookmarkManagerConfig, SessionConfig, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + NotificationFilter } export default forExport diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index da9c2572f..72ba0d861 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -24,10 +24,6 @@ type FilterKeys = ExcludeUnknown | 'ALL' type FilterInnerKeys = ExcludeUnknown | 'ALL' type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } -interface AuxFunctions { - disabled: () => NotificationFilter[] - serverDefault: () => NotificationFilter[] -} type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | @@ -54,7 +50,10 @@ type NotificationFilter = * @property {function(): Array} disabled Creates a configuration with notifications disabled * @property {function(): Array} serverDefault Creates a configuration for using the server default */ -const notificationFilter: SeverityDotCategoryFilters & AuxFunctions = { +const notificationFilter: SeverityDotCategoryFilters & { + disabled: () => NotificationFilter[] + serverDefault: () => NotificationFilter[] +} = { disabled: () => ['NONE'], serverDefault: () => ['SERVER_DEFAULT'], ALL: { diff --git a/packages/neo4j-driver-deno/lib/core/index.ts b/packages/neo4j-driver-deno/lib/core/index.ts index cd8be6ccb..ed6d35e02 100644 --- a/packages/neo4j-driver-deno/lib/core/index.ts +++ b/packages/neo4j-driver-deno/lib/core/index.ts @@ -70,6 +70,7 @@ import ResultSummary, { notificationCategory, notificationSeverityLevel } from './result-summary.ts' +import notificationFilter, { NotificationFilter } from './notification-filter.ts' import Result, { QueryResult, ResultObserver } from './result.ts' import ConnectionProvider from './connection-provider.ts' import Connection from './connection.ts' @@ -155,7 +156,8 @@ const forExport = { auth, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export { @@ -217,7 +219,8 @@ export { auth, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export type { @@ -231,7 +234,8 @@ export type { BookmarkManagerConfig, SessionConfig, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + NotificationFilter } export default forExport diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index bdedcf8d8..dc0492397 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -24,10 +24,6 @@ type FilterKeys = ExcludeUnknown | 'ALL' type FilterInnerKeys = ExcludeUnknown | 'ALL' type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } -interface AuxFunctions { - disabled: () => NotificationFilter[] - serverDefault: () => NotificationFilter[] -} type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | @@ -54,7 +50,10 @@ type NotificationFilter = * @property {function(): Array} disabled Creates a configuration with notifications disabled * @property {function(): Array} serverDefault Creates a configuration for using the server default */ -const notificationFilter: SeverityDotCategoryFilters & AuxFunctions = { +const notificationFilter: SeverityDotCategoryFilters & { + disabled: () => NotificationFilter[] + serverDefault: () => NotificationFilter[] +} = { disabled: () => ['NONE'], serverDefault: () => ['SERVER_DEFAULT'], ALL: { diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 29fae91d5..6789b22d9 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -77,7 +77,9 @@ import { notificationCategory, notificationSeverityLevel, NotificationSeverityLevel, - NotificationCategory + NotificationCategory, + notificationFilter, + NotificationFilter } from './core/index.ts' // @deno-types=./bolt-connection/types/index.d.ts import { @@ -472,7 +474,8 @@ const forExport = { Connection, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export { @@ -527,7 +530,8 @@ export { Connection, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export type { QueryResult, @@ -542,6 +546,7 @@ export type { BookmarkManagerConfig, SessionConfig, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + NotificationFilter } export default forExport diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 143f71a19..28a92a2d6 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -77,7 +77,9 @@ import { notificationCategory, notificationSeverityLevel, NotificationSeverityLevel, - NotificationCategory + NotificationCategory, + notificationFilter, + NotificationFilter } from 'neo4j-driver-core' import { DirectConnectionProvider, @@ -471,7 +473,8 @@ const forExport = { Connection, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export { @@ -526,7 +529,8 @@ export { Connection, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export type { QueryResult, @@ -541,6 +545,7 @@ export type { BookmarkManagerConfig, SessionConfig, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + NotificationFilter } export default forExport diff --git a/packages/neo4j-driver-lite/test/unit/index.test.ts b/packages/neo4j-driver-lite/test/unit/index.test.ts index 9b47ef823..3b1cb5a77 100644 --- a/packages/neo4j-driver-lite/test/unit/index.test.ts +++ b/packages/neo4j-driver-lite/test/unit/index.test.ts @@ -326,4 +326,13 @@ describe('index', () => { expect(neo4j.notificationCategory.GENERIC).toBeDefined() expect(neo4j.notificationCategory.UNKNOWN).toBeDefined() }) + + it('should export notificationFilter', () => { + expect(neo4j.notificationFilter).toBeDefined() + expect(neo4j.notificationFilter.ALL).toBeDefined() + expect(neo4j.notificationFilter.INFORMATION).toBeDefined() + expect(neo4j.notificationFilter.WARNING).toBeDefined() + expect(neo4j.notificationFilter.disabled).toBeDefined() + expect(neo4j.notificationFilter.serverDefault).toBeDefined() + }) }) diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 264c830a6..7d6376b4f 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -63,7 +63,8 @@ import { ManagedTransaction, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } from 'neo4j-driver-core' import { DirectConnectionProvider, @@ -459,7 +460,8 @@ const forExport = { DateTime, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export { @@ -515,6 +517,7 @@ export { DateTime, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export default forExport diff --git a/packages/neo4j-driver/test/types/index.test.ts b/packages/neo4j-driver/test/types/index.test.ts index 81b0d4dae..11e0f5c18 100644 --- a/packages/neo4j-driver/test/types/index.test.ts +++ b/packages/neo4j-driver/test/types/index.test.ts @@ -32,7 +32,9 @@ import { notificationSeverityLevel, NotificationSeverityLevel, notificationCategory, - NotificationCategory + NotificationCategory, + notificationFilter, + NotificationFilter } from '../../types/index' import Driver from '../../types/driver' @@ -114,3 +116,57 @@ const genericCategory: NotificationCategory = notificationCategory.GENERIC const unrecognizedCategory: NotificationCategory = notificationCategory.UNRECOGNIZED const unsupportedCategory: NotificationCategory = notificationCategory.UNSUPPORTED const unknownCategory: NotificationCategory = notificationCategory.UNKNOWN + +const allAllString: string = notificationFilter.ALL.ALL +const allDeprecationString: string = notificationFilter.ALL.DEPRECATION +const allPerformanceString: string = notificationFilter.ALL.PERFORMANCE +const allGenericString: string = notificationFilter.ALL.GENERIC +const allUnrecognizedString: string = notificationFilter.ALL.UNRECOGNIZED +const allUnsupportedString: string = notificationFilter.ALL.UNSUPPORTED +const allQueryString: string = notificationFilter.ALL.QUERY +const allHintString: string = notificationFilter.ALL.HINT + +const allAllFilter: NotificationFilter = notificationFilter.ALL.ALL +const allDeprecationFilter: NotificationFilter = notificationFilter.ALL.DEPRECATION +const allPerformanceFilter: NotificationFilter = notificationFilter.ALL.PERFORMANCE +const allGenericFilter: NotificationFilter = notificationFilter.ALL.GENERIC +const allUnrecognizedFilter: NotificationFilter = notificationFilter.ALL.UNRECOGNIZED +const allUnsupportedFilter: NotificationFilter = notificationFilter.ALL.UNSUPPORTED +const allQueryFilter: NotificationFilter = notificationFilter.ALL.QUERY +const allHintFilter: NotificationFilter = notificationFilter.ALL.HINT + +const informationAllString: string = notificationFilter.INFORMATION.ALL +const informationDeprecationString: string = notificationFilter.INFORMATION.DEPRECATION +const informationPerformanceString: string = notificationFilter.INFORMATION.PERFORMANCE +const informationGenericString: string = notificationFilter.INFORMATION.GENERIC +const informationUnrecognizedString: string = notificationFilter.INFORMATION.UNRECOGNIZED +const informationUnsupportedString: string = notificationFilter.INFORMATION.UNSUPPORTED +const informationQueryString: string = notificationFilter.INFORMATION.QUERY +const informationHintString: string = notificationFilter.INFORMATION.HINT + +const informationAllFilter: NotificationFilter = notificationFilter.INFORMATION.ALL +const informationDeprecationFilter: NotificationFilter = notificationFilter.INFORMATION.DEPRECATION +const informationPerformanceFilter: NotificationFilter = notificationFilter.INFORMATION.PERFORMANCE +const informationGenericFilter: NotificationFilter = notificationFilter.INFORMATION.GENERIC +const informationUnrecognizedFilter: NotificationFilter = notificationFilter.INFORMATION.UNRECOGNIZED +const informationUnsupportedFilter: NotificationFilter = notificationFilter.INFORMATION.UNSUPPORTED +const informationQueryFilter: NotificationFilter = notificationFilter.INFORMATION.QUERY +const informationHintFilter: NotificationFilter = notificationFilter.INFORMATION.HINT + +const warningAllString: string = notificationFilter.WARNING.ALL +const warningDeprecationString: string = notificationFilter.WARNING.DEPRECATION +const warningPerformanceString: string = notificationFilter.WARNING.PERFORMANCE +const warningGenericString: string = notificationFilter.WARNING.GENERIC +const warningUnrecognizedString: string = notificationFilter.WARNING.UNRECOGNIZED +const warningUnsupportedString: string = notificationFilter.WARNING.UNSUPPORTED +const warningQueryString: string = notificationFilter.WARNING.QUERY +const warningHintString: string = notificationFilter.WARNING.HINT + +const warningAllFilter: NotificationFilter = notificationFilter.WARNING.ALL +const warningDeprecationFilter: NotificationFilter = notificationFilter.WARNING.DEPRECATION +const warningPerformanceFilter: NotificationFilter = notificationFilter.WARNING.PERFORMANCE +const warningGenericFilter: NotificationFilter = notificationFilter.WARNING.GENERIC +const warningUnrecognizedFilter: NotificationFilter = notificationFilter.WARNING.UNRECOGNIZED +const warningUnsupportedFilter: NotificationFilter = notificationFilter.WARNING.UNSUPPORTED +const warningQueryFilter: NotificationFilter = notificationFilter.WARNING.QUERY +const warningHintFilter: NotificationFilter = notificationFilter.WARNING.HINT diff --git a/packages/neo4j-driver/types/index.d.ts b/packages/neo4j-driver/types/index.d.ts index e0b256735..80631aa7a 100644 --- a/packages/neo4j-driver/types/index.d.ts +++ b/packages/neo4j-driver/types/index.d.ts @@ -68,7 +68,9 @@ import { notificationCategory, notificationSeverityLevel, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + notificationFilter, + NotificationFilter } from 'neo4j-driver-core' import { AuthToken, @@ -231,6 +233,7 @@ declare const forExport: { bookmarkManager: typeof bookmarkManager notificationCategory: typeof notificationCategory notificationSeverityLevel: typeof notificationSeverityLevel + notificationFilter: typeof notificationFilter } export { @@ -294,7 +297,8 @@ export { isDateTime, bookmarkManager, notificationCategory, - notificationSeverityLevel + notificationSeverityLevel, + notificationFilter } export type { @@ -302,7 +306,8 @@ export type { BookmarkManagerConfig, SessionConfig, NotificationCategory, - NotificationSeverityLevel + NotificationSeverityLevel, + NotificationFilter } export default forExport From 33887c96c02cf93e34d075e693b3b5229fc54403 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 15:44:52 +0100 Subject: [PATCH 16/27] Sanitize notification filters --- .../src/bolt/bolt-protocol-v5x1.js | 29 +++- .../test/bolt/bolt-protocol-v5x1.test.js | 160 +++++++++--------- .../bolt/bolt-protocol-v5x1.js | 29 +++- 3 files changed, 136 insertions(+), 82 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index f8bc685a1..abaa4199f 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -61,7 +61,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { this.write( RequestMessage.hello5x1(authToken, { userAgent, - notificationFilters, + notificationFilters: sanitizeNotificationFilters(notificationFilters), routing: this._serversideRouting }), observer, @@ -118,7 +118,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { database, mode, impersonatedUser, - notificationFilters + notificationFilters: sanitizeNotificationFilters(notificationFilters) }), observer, flushRun && flush @@ -153,7 +153,14 @@ export default class BoltProtocol extends BoltProtocolV5x0 { observer.prepareToHandleSingleResponse() this.write( - RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters }), + RequestMessage.begin({ + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters: sanitizeNotificationFilters(notificationFilters) + }), observer, true ) @@ -161,3 +168,19 @@ export default class BoltProtocol extends BoltProtocolV5x0 { return observer } } + +function sanitizeNotificationFilters (filters) { + if (filters == null || filters === []) { + return filters + } + + if (filters[0] === 'NONE') { + return [] + } + + if (filters[0] === 'SERVER_DEFAULT') { + return undefined + } + + return filters.map(filter => filter.replace(/^ALL\./, '*.').replace(/\.ALL$/, '.*')) +} 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 f5f6a1e1a..b2e825d6e 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -181,47 +181,47 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([false, true]) }) - it('should run a with notification filters', () => { - const database = 'testdb' - const notificationFilters = ['*.*', 'WARNING.*'] - 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 BoltProtocolV5x1(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, - notificationFilters - }) + it.each(notificationFiltersFixtures())('should run a with notification filters', + (notificationFilters, sentNotificationFilters) => { + 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 BoltProtocolV5x1(recorder, null, false) + utils.spyProtocolWrite(protocol) - protocol.verifyMessageCount(2) + const query = 'RETURN $x, $y' + const parameters = { x: 'x', y: 'y' } - expect(protocol.messages[0]).toBeMessage( - RequestMessage.runWithMetadata(query, parameters, { + const observer = protocol.run(query, parameters, { bookmarks, txConfig, database, mode: WRITE, notificationFilters }) - ) - expect(protocol.messages[1]).toBeMessage(RequestMessage.pull()) - expect(protocol.observers).toEqual([observer, observer]) - expect(protocol.flushes).toEqual([false, true]) - }) + + protocol.verifyMessageCount(2) + + expect(protocol.messages[0]).toBeMessage( + RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + database, + mode: WRITE, + notificationFilters: sentNotificationFilters + }) + ) + 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' @@ -283,36 +283,36 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([true]) }) - it('should begin a transaction with notification filters', () => { - const database = 'testdb' - const notificationFilters = ['*.*', 'WARNING.*'] - 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 BoltProtocolV5x1(recorder, null, false) - utils.spyProtocolWrite(protocol) + it.each(notificationFiltersFixtures())('should begin a transaction with notification filters', + (notificationFilters, sentNotificationFilters) => { + 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 BoltProtocolV5x1(recorder, null, false) + utils.spyProtocolWrite(protocol) - const observer = protocol.beginTransaction({ - bookmarks, - txConfig, - database, - mode: WRITE, - notificationFilters - }) + const observer = protocol.beginTransaction({ + bookmarks, + txConfig, + database, + mode: WRITE, + notificationFilters + }) - protocol.verifyMessageCount(1) - expect(protocol.messages[0]).toBeMessage( - RequestMessage.begin({ bookmarks, txConfig, database, mode: WRITE, notificationFilters }) - ) - expect(protocol.observers).toEqual([observer]) - expect(protocol.flushes).toEqual([true]) - }) + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.begin({ bookmarks, txConfig, database, mode: WRITE, notificationFilters: sentNotificationFilters }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) it('should return correct bolt version number', () => { const protocol = new BoltProtocolV5x1(null, null, false) @@ -352,24 +352,24 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([true]) }) - it('should initialize connection with notification filters', () => { - const recorder = new utils.MessageRecordingConnection() - const protocol = new BoltProtocolV5x1(recorder, null, false) - utils.spyProtocolWrite(protocol) + it.each(notificationFiltersFixtures())('should initialize connection with notification filters [%s]', + (notificationFilters, sentNotificationFilters) => { + 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 notificationFilters = ['*.*', 'WARNING.*'] + const clientName = 'js-driver/1.2.3' + const authToken = { username: 'neo4j', password: 'secret' } - const observer = protocol.initialize({ userAgent: clientName, authToken, notificationFilters }) + const observer = protocol.initialize({ userAgent: clientName, authToken, notificationFilters }) - protocol.verifyMessageCount(1) - expect(protocol.messages[0]).toBeMessage( - RequestMessage.hello5x1(authToken, { routing: false, userAgent: clientName, notificationFilters }) - ) - expect(protocol.observers).toEqual([observer]) - expect(protocol.flushes).toEqual([true]) - }) + protocol.verifyMessageCount(1) + expect(protocol.messages[0]).toBeMessage( + RequestMessage.hello5x1(authToken, { routing: false, userAgent: clientName, notificationFilters: sentNotificationFilters }) + ) + expect(protocol.observers).toEqual([observer]) + expect(protocol.flushes).toEqual([true]) + }) it('should begin a transaction', () => { const bookmarks = new Bookmarks([ @@ -1119,4 +1119,12 @@ describe('#unit BoltProtocolV5x1', () => { expect(unpacked).toEqual(struct) }) }) + + function notificationFiltersFixtures () { + return [ + [['ALL.ALL', 'WARNING.ALL', 'ALL.HINT', 'INFORMATION.QUERY'], ['*.*', 'WARNING.*', '*.HINT', 'INFORMATION.QUERY']], + [['NONE'], []], + [['SERVER_DEFAULT'], undefined] + ] + } }) 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 d2d66c255..c19f445ba 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 @@ -61,7 +61,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { this.write( RequestMessage.hello5x1(authToken, { userAgent, - notificationFilters, + notificationFilters: sanitizeNotificationFilters(notificationFilters), routing: this._serversideRouting }), observer, @@ -118,7 +118,7 @@ export default class BoltProtocol extends BoltProtocolV5x0 { database, mode, impersonatedUser, - notificationFilters + notificationFilters: sanitizeNotificationFilters(notificationFilters) }), observer, flushRun && flush @@ -153,7 +153,14 @@ export default class BoltProtocol extends BoltProtocolV5x0 { observer.prepareToHandleSingleResponse() this.write( - RequestMessage.begin({ bookmarks, txConfig, database, mode, impersonatedUser, notificationFilters }), + RequestMessage.begin({ + bookmarks, + txConfig, + database, + mode, + impersonatedUser, + notificationFilters: sanitizeNotificationFilters(notificationFilters) + }), observer, true ) @@ -161,3 +168,19 @@ export default class BoltProtocol extends BoltProtocolV5x0 { return observer } } + +function sanitizeNotificationFilters (filters) { + if (filters == null || filters === []) { + return filters + } + + if (filters[0] === 'NONE') { + return [] + } + + if (filters[0] === 'SERVER_DEFAULT') { + return undefined + } + + return filters.map(filter => filter.replace(/^ALL\./, '*.').replace(/\.ALL$/, '.*')) +} From 2dd68168914f7d52a82c151e8ee4d6685ae0a376 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 17:05:26 +0100 Subject: [PATCH 17/27] Validate notification filters while create sessions and drivers --- packages/core/src/driver.ts | 37 +++- packages/core/src/notification-filter.ts | 20 ++ packages/core/test/driver.test.ts | 186 +++++++++++++++++- packages/neo4j-driver-deno/lib/core/driver.ts | 37 +++- .../lib/core/notification-filter.ts | 20 ++ 5 files changed, 297 insertions(+), 3 deletions(-) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 65fb6d393..19d592341 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -20,7 +20,7 @@ import ConnectionProvider from './connection-provider' import { Bookmarks } from './internal/bookmarks' import ConfiguredCustomResolver from './internal/resolver/configured-custom-resolver' -import { NotificationFilter } from './notification-filter' +import { isValidFilter, NotificationFilter } from './notification-filter' import { ACCESS_MODE_READ, @@ -42,6 +42,7 @@ import { } from './types' import { ServerAddress } from './internal/server-address' import BookmarkManager from './bookmark-manager' +import { stringify } from './json' const DEFAULT_MAX_CONNECTION_LIFETIME: number = 60 * 60 * 1000 // 1 hour @@ -459,6 +460,7 @@ class Driver { bookmarkManager?: BookmarkManager notificationFilters?: NotificationFilter[] }): Session { + validateNotificationFilters(notificationFilters) const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() const bookmarks = bookmarkOrBookmarks != null @@ -516,6 +518,9 @@ function validateConfig (config: any, log: Logger): any { 'where a new connection is created while it is acquired' ) } + + validateNotificationFilters(config.notificationFilters) + return config } @@ -604,6 +609,36 @@ function createHostNameResolver (config: any): ConfiguredCustomResolver { return new ConfiguredCustomResolver(config.resolver) } +/** + * @private + */ +function validateNotificationFilters (filters: any): void { + if (filters == null || filters === []) { + return filters + } + + if (!Array.isArray(filters)) { + throw new TypeError('Expect "notificationFilters" to be instance of Array.') + } + + if (filters.length > 1) { + if (filters.includes('NONE')) { + throw new Error('Expect "notificationFilters" to not have "NONE" configured along with other filters.') + } + + if (filters.includes('SERVER_DEFAULT')) { + throw new Error('Expect "notificationFilters" to not have "SERVER_DEFAULT" configured along with other filters.') + } + } + + const invalidFilters = filters.filter(filter => !isValidFilter(filter)) + + if (invalidFilters.length > 0) { + const invalidValuesString = invalidFilters.map(stringify).join(', ') + throw new Error(`Invalid "notificationFilters". Invalid values: ${invalidValuesString}`) + } +} + export { Driver, READ, WRITE } export type { SessionConfig } export default Driver diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index 72ba0d861..1e85eb97b 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -90,8 +90,28 @@ const notificationFilter: SeverityDotCategoryFilters & { Object.freeze(notificationFilter) +const filters = Object.values(notificationFilter) + .map(value => { + if (typeof value === 'function') { + return value() + } + return Object.values(value) + }) + .reduce((previous, current) => [...previous, ...current], []) + +/** + * @private + */ +function isValidFilter (value: any): boolean { + return filters.includes(value) +} + export default notificationFilter +export { + isValidFilter +} + export type { NotificationFilter } diff --git a/packages/core/test/driver.test.ts b/packages/core/test/driver.test.ts index c7c4c4b71..40faa6cb0 100644 --- a/packages/core/test/driver.test.ts +++ b/packages/core/test/driver.test.ts @@ -17,7 +17,7 @@ * limitations under the License. */ /* eslint-disable @typescript-eslint/promise-function-async */ -import { bookmarkManager, ConnectionProvider, newError, ServerInfo, Session } from '../src' +import { bookmarkManager, ConnectionProvider, newError, NotificationFilter, notificationFilter, ServerInfo, Session } from '../src' import Driver, { READ } from '../src/driver' import { Bookmarks } from '../src/internal/bookmarks' import { Logger } from '../src/internal/logger' @@ -155,6 +155,106 @@ describe('Driver', () => { } }) }) + + describe('when set config.notificationFilters', () => { + it.each([ + [], + undefined, + notificationFilter.disabled(), + notificationFilter.serverDefault(), + [notificationFilter.ALL.ALL, notificationFilter.INFORMATION.GENERIC], + ['WARNING.QUERY', 'INFORMATION.GENERIC'] + ])('should send valid "notificationFilters" to the session', async (notificationFilters?: NotificationFilter[]) => { + const driver = new Driver( + META_INFO, + { ...CONFIG }, + mockCreateConnectonProvider(connectionProvider), + createSession + ) + + const session = driver.session({ notificationFilters }) + + try { + expect(createSession).toBeCalledWith(expect.objectContaining({ + notificationFilters + })) + } finally { + await session.close() + await driver.close() + } + }) + + it.each([ + notificationFilter.ALL.DEPRECATION, + 'WARNING.QUERY', + 'INFO', + 1234, + { 'WARNING.QUERY': notificationFilter.WARNING.QUERY }, + () => [notificationFilter.ALL.DEPRECATION] + ])('should thrown when "notificationFilters" is not an array', async (notificationFilters?: any) => { + const driver = new Driver( + META_INFO, + { ...CONFIG }, + mockCreateConnectonProvider(connectionProvider), + createSession + ) + + try { + expect(() => driver.session({ notificationFilters })).toThrow(new TypeError('Expect "notificationFilters" to be instance of Array.')) + } finally { + await driver.close() + } + }) + + it('should throw when "NONE" is configured with other filters', async () => { + const driver = new Driver( + META_INFO, + { ...CONFIG }, + mockCreateConnectonProvider(connectionProvider), + createSession + ) + + try { + expect(() => driver.session({ notificationFilters: ['NONE', 'ALL.DEPRECATION'] })) + .toThrow(new Error('Expect "notificationFilters" to not have "NONE" configured along with other filters.')) + } finally { + await driver.close() + } + }) + + it('should throw when "SERVER_DEFAULT" is configured with other filters', async () => { + const driver = new Driver( + META_INFO, + { ...CONFIG }, + mockCreateConnectonProvider(connectionProvider), + createSession + ) + + try { + expect(() => driver.session({ notificationFilters: ['SERVER_DEFAULT', 'ALL.DEPRECATION'] })) + .toThrow(new Error('Expect "notificationFilters" to not have "SERVER_DEFAULT" configured along with other filters.')) + } finally { + await driver.close() + } + }) + + it('should throw when invalid filters are configured', async () => { + const driver = new Driver( + META_INFO, + { ...CONFIG }, + mockCreateConnectonProvider(connectionProvider), + createSession + ) + + try { + // @ts-expect-error + expect(() => driver.session({ notificationFilters: ['ALL.DEPRECATION', 'ABC', 123] })) + .toThrow(new Error('Invalid "notificationFilters". Invalid values: "ABC", 123')) + } finally { + await driver.close() + } + }) + }) }) it.each([ @@ -307,6 +407,90 @@ describe('Driver', () => { promise?.catch(_ => 'Do nothing').finally(() => { }) }) + describe('constructor', () => { + describe('when set config.notificationFilters', () => { + it.each([ + [], + undefined, + notificationFilter.disabled(), + notificationFilter.serverDefault(), + [notificationFilter.ALL.ALL, notificationFilter.INFORMATION.GENERIC], + ['WARNING.QUERY', 'INFORMATION.GENERIC'] + ])('should send valid "notificationFilters" to the connection provider', async (notificationFilters?: NotificationFilter[]) => { + const createConnectionProviderMock = jest.fn(mockCreateConnectonProvider(connectionProvider)) + const driver = new Driver( + META_INFO, + { notificationFilters }, + createConnectionProviderMock, + createSession + ) + + driver._getOrCreateConnectionProvider() + + expect(createConnectionProviderMock).toHaveBeenCalledWith( + expect.any(Number), + expect.objectContaining({ notificationFilters }), + expect.any(Object), + expect.any(Object) + ) + + await driver.close() + }) + + it.each([ + notificationFilter.ALL.DEPRECATION, + 'WARNING.QUERY', + 'INFO', + 1234, + { 'WARNING.QUERY': notificationFilter.WARNING.QUERY }, + () => [notificationFilter.ALL.DEPRECATION] + ])('should thrown when "notificationFilters" is not an array', async (notificationFilters?: any) => { + const createConnectionProviderMock = mockCreateConnectonProvider(connectionProvider) + + expect(() => new Driver( + META_INFO, + { notificationFilters }, + createConnectionProviderMock, + createSession + )).toThrow(new TypeError('Expect "notificationFilters" to be instance of Array.')) + }) + + it('should throw when "NONE" is configured with other filters', async () => { + const createConnectionProviderMock = mockCreateConnectonProvider(connectionProvider) + + expect(() => new Driver( + META_INFO, + { notificationFilters: ['NONE', 'ALL.DEPRECATION'] }, + createConnectionProviderMock, + createSession + )).toThrow(new Error('Expect "notificationFilters" to not have "NONE" configured along with other filters.')) + }) + + it('should throw when "SERVER_DEFAULT" is configured with other filters', async () => { + const createConnectionProviderMock = mockCreateConnectonProvider(connectionProvider) + + expect(() => new Driver( + META_INFO, + { notificationFilters: ['ALL.DEPRECATION', 'SERVER_DEFAULT'] }, + createConnectionProviderMock, + createSession + )).toThrow(new Error('Expect "notificationFilters" to not have "SERVER_DEFAULT" configured along with other filters.')) + }) + + it('should throw when invalid filters are configured', async () => { + const createConnectionProviderMock = mockCreateConnectonProvider(connectionProvider) + + expect(() => new Driver( + META_INFO, + // @ts-expect-error + { notificationFilters: ['ALL.DEPRECATION', 'ABC', 123] }, + createConnectionProviderMock, + createSession + )).toThrow(new Error('Invalid "notificationFilters". Invalid values: "ABC", 123')) + }) + }) + }) + function mockCreateConnectonProvider (connectionProvider: ConnectionProvider) { return ( id: number, diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index e96dc2fb2..0a262949c 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -20,7 +20,7 @@ import ConnectionProvider from './connection-provider.ts' import { Bookmarks } from './internal/bookmarks.ts' import ConfiguredCustomResolver from './internal/resolver/configured-custom-resolver.ts' -import { NotificationFilter } from './notification-filter.ts' +import { isValidFilter, NotificationFilter } from './notification-filter.ts' import { ACCESS_MODE_READ, @@ -42,6 +42,7 @@ import { } from './types.ts' import { ServerAddress } from './internal/server-address.ts' import BookmarkManager from './bookmark-manager.ts' +import { stringify } from './json.ts' const DEFAULT_MAX_CONNECTION_LIFETIME: number = 60 * 60 * 1000 // 1 hour @@ -459,6 +460,7 @@ class Driver { bookmarkManager?: BookmarkManager notificationFilters?: NotificationFilter[] }): Session { + validateNotificationFilters(notificationFilters) const sessionMode = Session._validateSessionMode(defaultAccessMode) const connectionProvider = this._getOrCreateConnectionProvider() const bookmarks = bookmarkOrBookmarks != null @@ -516,6 +518,9 @@ function validateConfig (config: any, log: Logger): any { 'where a new connection is created while it is acquired' ) } + + validateNotificationFilters(config.notificationFilters) + return config } @@ -604,6 +609,36 @@ function createHostNameResolver (config: any): ConfiguredCustomResolver { return new ConfiguredCustomResolver(config.resolver) } +/** + * @private + */ +function validateNotificationFilters (filters: any): void { + if (filters == null || filters === []) { + return filters + } + + if (!Array.isArray(filters)) { + throw new TypeError('Expect "notificationFilters" to be instance of Array.') + } + + if (filters.length > 1) { + if (filters.includes('NONE')) { + throw new Error('Expect "notificationFilters" to not have "NONE" configured along with other filters.') + } + + if (filters.includes('SERVER_DEFAULT')) { + throw new Error('Expect "notificationFilters" to not have "SERVER_DEFAULT" configured along with other filters.') + } + } + + const invalidFilters = filters.filter(filter => !isValidFilter(filter)) + + if (invalidFilters.length > 0) { + const invalidValuesString = invalidFilters.map(stringify).join(', ') + throw new Error(`Invalid "notificationFilters". Invalid values: ${invalidValuesString}`) + } +} + export { Driver, READ, WRITE } export type { SessionConfig } export default Driver diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index dc0492397..8bd3527c9 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -90,8 +90,28 @@ const notificationFilter: SeverityDotCategoryFilters & { Object.freeze(notificationFilter) +const filters = Object.values(notificationFilter) + .map(value => { + if (typeof value === 'function') { + return value() + } + return Object.values(value) + }) + .reduce((previous, current) => [...previous, ...current], []) + +/** + * @private + */ +function isValidFilter (value: any): boolean { + return filters.includes(value) +} + export default notificationFilter +export { + isValidFilter +} + export type { NotificationFilter } From cdfe17e911c273fbe8ce627cb346edc6ba6aec91 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 17:46:28 +0100 Subject: [PATCH 18/27] Add tests to verify filters are being send to the protocol --- packages/core/test/session.test.ts | 57 ++++++++++++++++++++++++-- packages/core/test/transaction.test.ts | 39 +++++++++++++++--- 2 files changed, 88 insertions(+), 8 deletions(-) diff --git a/packages/core/test/session.test.ts b/packages/core/test/session.test.ts index 6d1d1f417..1364e7231 100644 --- a/packages/core/test/session.test.ts +++ b/packages/core/test/session.test.ts @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ConnectionProvider, Session, Connection, TransactionPromise, Transaction, BookmarkManager, bookmarkManager } from '../src' +import { ConnectionProvider, Session, Connection, TransactionPromise, Transaction, BookmarkManager, bookmarkManager, notificationFilter, NotificationFilter } from '../src' import { bookmarks } from '../src/internal' import { ACCESS_MODE_READ, FETCH_ALL } from '../src/internal/constants' import ManagedTransaction from '../src/transaction-managed' @@ -446,6 +446,30 @@ describe('session', () => { expect(updateBookmarksSpy).not.toBeCalled() }) + + it.each([ + undefined, + [], + [notificationFilter.ALL.ALL], + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + ])('should call run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { + const connection = mockBeginWithSuccess(newFakeConnection()) + + const { session } = setupSession({ + connection, + beginTx: false, + database: 'neo4j', + notificationFilters + }) + + await session.beginTransaction() + + expect(connection.seenBeginTransaction[0][0]).toEqual( + expect.objectContaining({ + notificationFilters + }) + ) + }) }) describe('.commit()', () => { @@ -839,6 +863,30 @@ describe('session', () => { expect(updateBookmarksSpy).not.toBeCalled() }) + + it.each([ + undefined, + [], + [notificationFilter.ALL.ALL], + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + ])('should call run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { + const connection = newFakeConnection() + + const { session } = setupSession({ + connection, + beginTx: false, + database: 'neo4j', + notificationFilters + }) + + await session.run('query') + + expect(connection.seenProtocolOptions[0]).toEqual( + expect.objectContaining({ + notificationFilters + }) + ) + }) }) }) @@ -890,7 +938,8 @@ function setupSession ({ fetchSize = 1000, database = '', lastBookmarks = bookmarks.Bookmarks.empty(), - bookmarkManager + bookmarkManager, + notificationFilters }: { connection: Connection beginTx?: boolean @@ -898,6 +947,7 @@ function setupSession ({ lastBookmarks?: bookmarks.Bookmarks database?: string bookmarkManager?: BookmarkManager + notificationFilters?: NotificationFilter[] }): { session: Session, connectionProvider: ConnectionProvider } { const connectionProvider = new ConnectionProvider() connectionProvider.acquireConnection = jest.fn(async () => await Promise.resolve(connection)) @@ -911,7 +961,8 @@ function setupSession ({ config: {}, reactive: false, bookmarks: lastBookmarks, - bookmarkManager + bookmarkManager, + notificationFilters }) if (beginTx) { diff --git a/packages/core/test/transaction.test.ts b/packages/core/test/transaction.test.ts index d61459ff3..1b9005323 100644 --- a/packages/core/test/transaction.test.ts +++ b/packages/core/test/transaction.test.ts @@ -17,7 +17,7 @@ * limitations under the License. */ -import { ConnectionProvider, newError, Transaction, TransactionPromise } from '../src' +import { ConnectionProvider, newError, NotificationFilter, notificationFilter, Transaction, TransactionPromise } from '../src' import { Bookmarks } from '../src/internal/bookmarks' import { ConnectionHolder } from '../src/internal/connection-holder' import { TxConfig } from '../src/internal/tx-config' @@ -375,6 +375,28 @@ function testTx (transactionName: string, newTransaction: expect(connection.seenBeginTransaction.length).toEqual(1) expect(connection.seenQueries.length).toEqual(1) }) + + it.each([ + undefined, + [], + [notificationFilter.ALL.ALL], + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + ])('should call not run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { + const connection = newFakeConnection() + const tx = newTransaction({ + connection, + notificationFilters + }) + + tx._begin(async () => Bookmarks.empty(), TxConfig.empty()) + + await tx.run('RETURN 1') + expect(connection.seenProtocolOptions[0]).not.toEqual( + expect.objectContaining({ + notificationFilters + }) + ) + }) }) describe('.close()', () => { @@ -467,6 +489,7 @@ type TransactionFactory = (_: { fetchSize?: number highRecordWatermark?: number lowRecordWatermark?: number + notificationFilters?: NotificationFilter[] }) => T function newTransactionPromise ({ @@ -474,13 +497,15 @@ function newTransactionPromise ({ fetchSize = 1000, highRecordWatermark = 700, lowRecordWatermark = 300, - errorResolvingConnection = undefined + errorResolvingConnection = undefined, + notificationFilters }: { connection?: FakeConnection fetchSize?: number highRecordWatermark?: number lowRecordWatermark?: number errorResolvingConnection?: Error + notificationFilters?: NotificationFilter[] }): TransactionPromise { const connectionProvider = new ConnectionProvider() // @ts-expect-error @@ -504,7 +529,8 @@ function newTransactionPromise ({ fetchSize, impersonatedUser: '', highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }) return transaction @@ -514,12 +540,14 @@ function newRegularTransaction ({ connection, fetchSize = 1000, highRecordWatermark = 700, - lowRecordWatermark = 300 + lowRecordWatermark = 300, + notificationFilters }: { connection: FakeConnection fetchSize?: number highRecordWatermark?: number lowRecordWatermark?: number + notificationFilters?: NotificationFilter[] }): Transaction { const connectionProvider = new ConnectionProvider() connectionProvider.acquireConnection = async () => await Promise.resolve(connection) @@ -537,7 +565,8 @@ function newRegularTransaction ({ fetchSize, impersonatedUser: '', highRecordWatermark, - lowRecordWatermark + lowRecordWatermark, + notificationFilters }) return transaction From 1152f59ce8d948192be477e8a774ce63078bd023 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Thu, 3 Nov 2022 18:17:22 +0100 Subject: [PATCH 19/27] improve filters documentation --- packages/core/src/notification-filter.ts | 20 ++++++++++++++++++- .../lib/core/notification-filter.ts | 20 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index 1e85eb97b..4d83fcb67 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -35,6 +35,9 @@ type NotificationFilter = 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' /** + * + * Notifications filters used during the {@link Driver} and {@link Session} configuration. + * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | @@ -43,12 +46,27 @@ type NotificationFilter = * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter */ - +/** + * Defines the category filters available for a given severity level filter + * + * @typedef {object} CategoryFiltersInSeverityLevel + * @property {NotificationFilter} ALL + * @property {NotificationFilter} DEPRECATION + * @property {NotificationFilter} GENERIC + * @property {NotificationFilter} HINT + * @property {NotificationFilter} PERFORMANCE + * @property {NotificationFilter} QUERY + * @property {NotificationFilter} UNRECOGNIZED + * @property {NotificationFilter} UNSUPPORTED + */ /** * Constants that represents the available notification filters * * @property {function(): Array} disabled Creates a configuration with notifications disabled * @property {function(): Array} serverDefault Creates a configuration for using the server default + * @property {CategoryFiltersInSeverityLevel} ALL Filters with all severities for category + * @property {CategoryFiltersInSeverityLevel} WARNING Filters with warning severity for category + * @property {CategoryFiltersInSeverityLevel} INFORMATION Filters with information severity for category */ const notificationFilter: SeverityDotCategoryFilters & { disabled: () => NotificationFilter[] diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index 8bd3527c9..1d7b5ecd8 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -35,6 +35,9 @@ type NotificationFilter = 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' /** + * + * Notifications filters used during the {@link Driver} and {@link Session} configuration. + * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | @@ -43,12 +46,27 @@ type NotificationFilter = * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter */ - +/** + * Defines the category filters available for a given severity level filter + * + * @typedef {object} CategoryFiltersInSeverityLevel + * @property {NotificationFilter} ALL + * @property {NotificationFilter} DEPRECATION + * @property {NotificationFilter} GENERIC + * @property {NotificationFilter} HINT + * @property {NotificationFilter} PERFORMANCE + * @property {NotificationFilter} QUERY + * @property {NotificationFilter} UNRECOGNIZED + * @property {NotificationFilter} UNSUPPORTED + */ /** * Constants that represents the available notification filters * * @property {function(): Array} disabled Creates a configuration with notifications disabled * @property {function(): Array} serverDefault Creates a configuration for using the server default + * @property {CategoryFiltersInSeverityLevel} ALL Filters with all severities for category + * @property {CategoryFiltersInSeverityLevel} WARNING Filters with warning severity for category + * @property {CategoryFiltersInSeverityLevel} INFORMATION Filters with information severity for category */ const notificationFilter: SeverityDotCategoryFilters & { disabled: () => NotificationFilter[] From 8e7cb103e993411c7370895feb02b8bf7a7c4392 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Fri, 4 Nov 2022 10:14:17 +0100 Subject: [PATCH 20/27] Improve notification filter definition by deriving from category and severity --- packages/core/src/notification-filter.ts | 11 +++-------- .../lib/core/notification-filter.ts | 13 ++++--------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index 4d83fcb67..a43613da5 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -18,21 +18,16 @@ */ import { NotificationCategory, NotificationSeverityLevel } from './result-summary' +type Combine = `${Enum1}${Separator}${Enum2}` type ExcludeUnknown = Exclude type FilterKeys = ExcludeUnknown | 'ALL' type FilterInnerKeys = ExcludeUnknown | 'ALL' +type CombinedFilter = Combine type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } -type NotificationFilter = - 'NONE' | 'SERVER_DEFAULT' | - 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | - 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | - 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | - 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | - 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | - 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' +type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter /** * diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index 1d7b5ecd8..a9a95ec36 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -18,22 +18,17 @@ */ import { NotificationCategory, NotificationSeverityLevel } from './result-summary.ts' +type Combine = `${Enum1}${Separator}${Enum2}`; type ExcludeUnknown = Exclude type FilterKeys = ExcludeUnknown | 'ALL' type FilterInnerKeys = ExcludeUnknown | 'ALL' +type CombinedFilter = Combine type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } -type NotificationFilter = - 'NONE' | 'SERVER_DEFAULT' | - 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | - 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | - 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | - 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | - 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | - 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' - +type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter + /** * * Notifications filters used during the {@link Driver} and {@link Session} configuration. From 3fa1439f65d9cd0afc06284098cbb488b6e68052 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Fri, 4 Nov 2022 10:15:05 +0100 Subject: [PATCH 21/27] Adjust deno code --- packages/neo4j-driver-deno/lib/core/notification-filter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index a9a95ec36..a8b1f67fe 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -18,7 +18,7 @@ */ import { NotificationCategory, NotificationSeverityLevel } from './result-summary.ts' -type Combine = `${Enum1}${Separator}${Enum2}`; +type Combine = `${Enum1}${Separator}${Enum2}` type ExcludeUnknown = Exclude type FilterKeys = ExcludeUnknown | 'ALL' @@ -28,7 +28,7 @@ type CombinedFilter = Combine type SeverityDotCategoryFilters = { [key in FilterKeys]: { [k in FilterInnerKeys]: NotificationFilter } } type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter - + /** * * Notifications filters used during the {@link Driver} and {@link Session} configuration. From 362b1412e18c8808533eef609c5fd07a4f97ae16 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Fri, 4 Nov 2022 15:09:28 +0100 Subject: [PATCH 22/27] Add configuration docs --- packages/core/src/driver.ts | 60 +++++++++++++++++-- packages/core/src/index.ts | 4 +- packages/neo4j-driver-deno/lib/core/driver.ts | 60 +++++++++++++++++-- packages/neo4j-driver-deno/lib/core/index.ts | 4 +- packages/neo4j-driver-deno/lib/mod.ts | 6 +- packages/neo4j-driver-lite/src/index.ts | 6 +- packages/neo4j-driver/src/index.js | 6 +- 7 files changed, 127 insertions(+), 19 deletions(-) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 19d592341..ea53e3b7e 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -164,10 +164,10 @@ class SessionConfig { * Enabling it is done by supplying an BookmarkManager implementation instance to this param. * A default implementation could be acquired by calling the factory function {@link bookmarkManager}. * - * **Warning**: Share the same BookmarkManager instance accross all session can have a negative impact + * **Warning**: Share the same BookmarkManager instance across all session can have a negative impact * on performance since all the queries will wait for the latest changes being propagated across the cluster. * For keeping consistency between a group of queries, use {@link Session} for grouping them. - * For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for groupping them. + * For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for grouping them. * * @example * const bookmarkManager = neo4j.bookmarkManager() @@ -184,7 +184,7 @@ class SessionConfig { * * // Reading Driver User will wait of the changes being propagated to the server before RUN the query * // So the 'Driver User' person should exist in the Result, unless deleted. - * const linkedSesssion2 = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'}) + * const linkedSession2 = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'}) * * await linkedSession1.close() * await linkedSession2.close() @@ -197,8 +197,57 @@ class SessionConfig { this.bookmarkManager = undefined /** - * @todo docs + * Configure filter for {@link Notification} objects returned in {@link ResultSummary#notifications}. + * + * The filters are defined by "{@link NotificationSeverityLevel}.{@link NotificationCategory}" with the + * exception of the "UNKNOWN" severity and category. + * "ALL" is added for possible value of category and severity filters. + * + * Disabling the filters is done by setting this configuration to ["NONE"]. + * Using the default values is done by setting this configuration to ["SERVER_DEFAULT"]. + * + * Helper constants and methods are defined at {@link notificationFilter}. + * + * @example + * // disabling notifications + * const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) + * // EQUIVALENT TO: const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: ["NONE"] }) + * + * // using default server configuration + * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) + * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) + * // OR SIMPLY: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * + * // Enable all notifications + * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) + * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: ['ALL.ALL'] }) + * + * // Configuring for any categories with severity "WARNING", + * // or any severity with category "QUERY" + * // or severity "INFORMATION" and category "PERFORMANCE". + * const sessionFineConfigured = driver.session({ + * database: 'neo4j', + * notificationFilters: [ + * neo4j.notificationFilter.WARNING.ALL, + * neo4j.notificationFilter.ALL.QUERY, + * neo4j.notificationFilter.INFORMATION.PERFORMANCE + * ] + * }) + * + * // Configuring for any categories with severity "WARNING", + * // or any severity with category "QUERY" + * // or severity "INFORMATION" and category "PERFORMANCE". + * // const sessionFineConfigured = driver.session({ + * // database: 'neo4j', + * // notificationFilters: [ + * // 'WARNING.ALL', + * // 'ALL.QUERY', + * // 'INFORMATION.PERFORMANCE' + * // ] + * // }) + * * @type {NotificationFilter[]|undefined} + * @since 5.3 */ this.notificationFilters = undefined } @@ -639,6 +688,5 @@ function validateNotificationFilters (filters: any): void { } } -export { Driver, READ, WRITE } -export type { SessionConfig } +export { Driver, READ, WRITE, SessionConfig } export default Driver diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9f5464823..19bd3e9ce 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -220,7 +220,8 @@ export { bookmarkManager, notificationCategory, notificationSeverityLevel, - notificationFilter + notificationFilter, + SessionConfig } export type { @@ -232,7 +233,6 @@ export type { TransactionConfig, BookmarkManager, BookmarkManagerConfig, - SessionConfig, NotificationCategory, NotificationSeverityLevel, NotificationFilter diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 0a262949c..108000c86 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -164,10 +164,10 @@ class SessionConfig { * Enabling it is done by supplying an BookmarkManager implementation instance to this param. * A default implementation could be acquired by calling the factory function {@link bookmarkManager}. * - * **Warning**: Share the same BookmarkManager instance accross all session can have a negative impact + * **Warning**: Share the same BookmarkManager instance across all session can have a negative impact * on performance since all the queries will wait for the latest changes being propagated across the cluster. * For keeping consistency between a group of queries, use {@link Session} for grouping them. - * For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for groupping them. + * For keeping consistency between a group of sessions, use {@link BookmarkManager} instance for grouping them. * * @example * const bookmarkManager = neo4j.bookmarkManager() @@ -184,7 +184,7 @@ class SessionConfig { * * // Reading Driver User will wait of the changes being propagated to the server before RUN the query * // So the 'Driver User' person should exist in the Result, unless deleted. - * const linkedSesssion2 = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'}) + * const linkedSession2 = await linkedSession2.run('CREATE (p:Person {name: $name}) RETURN p', { name: 'Driver User'}) * * await linkedSession1.close() * await linkedSession2.close() @@ -197,8 +197,57 @@ class SessionConfig { this.bookmarkManager = undefined /** - * @todo docs + * Configure filter for {@link Notification} objects returned in {@link ResultSummary#notifications}. + * + * The filters are defined by "{@link NotificationSeverityLevel}.{@link NotificationCategory}" with the + * exception of the "UNKNOWN" severity and category. + * "ALL" is added for possible value of category and severity filters. + * + * Disabling the filters is done by setting this configuration to ["NONE"]. + * Using the default values is done by setting this configuration to ["SERVER_DEFAULT"]. + * + * Helper constants and methods are defined at {@link notificationFilter}. + * + * @example + * // disabling notifications + * const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) + * // EQUIVALENT TO: const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: ["NONE"] }) + * + * // using default server configuration + * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) + * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) + * // OR SIMPLY: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * + * // Enable all notifications + * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) + * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: ['ALL.ALL'] }) + * + * // Configuring for any categories with severity "WARNING", + * // or any severity with category "QUERY" + * // or severity "INFORMATION" and category "PERFORMANCE". + * const sessionFineConfigured = driver.session({ + * database: 'neo4j', + * notificationFilters: [ + * neo4j.notificationFilter.WARNING.ALL, + * neo4j.notificationFilter.ALL.QUERY, + * neo4j.notificationFilter.INFORMATION.PERFORMANCE + * ] + * }) + * + * // Configuring for any categories with severity "WARNING", + * // or any severity with category "QUERY" + * // or severity "INFORMATION" and category "PERFORMANCE". + * // const sessionFineConfigured = driver.session({ + * // database: 'neo4j', + * // notificationFilters: [ + * // 'WARNING.ALL', + * // 'ALL.QUERY', + * // 'INFORMATION.PERFORMANCE' + * // ] + * // }) + * * @type {NotificationFilter[]|undefined} + * @since 5.3 */ this.notificationFilters = undefined } @@ -639,6 +688,5 @@ function validateNotificationFilters (filters: any): void { } } -export { Driver, READ, WRITE } -export type { SessionConfig } +export { Driver, READ, WRITE, SessionConfig } export default Driver diff --git a/packages/neo4j-driver-deno/lib/core/index.ts b/packages/neo4j-driver-deno/lib/core/index.ts index ed6d35e02..85798c2b7 100644 --- a/packages/neo4j-driver-deno/lib/core/index.ts +++ b/packages/neo4j-driver-deno/lib/core/index.ts @@ -220,7 +220,8 @@ export { bookmarkManager, notificationCategory, notificationSeverityLevel, - notificationFilter + notificationFilter, + SessionConfig } export type { @@ -232,7 +233,6 @@ export type { TransactionConfig, BookmarkManager, BookmarkManagerConfig, - SessionConfig, NotificationCategory, NotificationSeverityLevel, NotificationFilter diff --git a/packages/neo4j-driver-deno/lib/mod.ts b/packages/neo4j-driver-deno/lib/mod.ts index 6789b22d9..3e6097a6c 100644 --- a/packages/neo4j-driver-deno/lib/mod.ts +++ b/packages/neo4j-driver-deno/lib/mod.ts @@ -224,7 +224,11 @@ const { * return ['127.0.0.1:8888', 'fallback.db.com:7687']; * }, * - * // Optionally override the default user agent name. + * // Configure filter for Notification objects returned in ResultSummary#notifications. + * // See SessionConfig#notificationFilters for usage instructions. + * notificationFilters: ['SERVER_DEFAULT'] + * + * // Optionally override the default user agent name. * userAgent: USER_AGENT * } * diff --git a/packages/neo4j-driver-lite/src/index.ts b/packages/neo4j-driver-lite/src/index.ts index 28a92a2d6..4f00800bc 100644 --- a/packages/neo4j-driver-lite/src/index.ts +++ b/packages/neo4j-driver-lite/src/index.ts @@ -223,7 +223,11 @@ const { * return ['127.0.0.1:8888', 'fallback.db.com:7687']; * }, * - * // Optionally override the default user agent name. + * // Configure filter for Notification objects returned in ResultSummary#notifications. + * // See SessionConfig#notificationFilters for usage instructions. + * notificationFilters: ['SERVER_DEFAULT'] + * + * // Optionally override the default user agent name. * userAgent: USER_AGENT * } * diff --git a/packages/neo4j-driver/src/index.js b/packages/neo4j-driver/src/index.js index 7d6376b4f..cbff2c9a2 100644 --- a/packages/neo4j-driver/src/index.js +++ b/packages/neo4j-driver/src/index.js @@ -203,7 +203,11 @@ const { * return ['127.0.0.1:8888', 'fallback.db.com:7687']; * }, * - * // Optionally override the default user agent name. + * // Configure filter for Notification objects returned in ResultSummary#notifications. + * // See SessionConfig#notificationFilters for usage instructions. + * notificationFilters: ['SERVER_DEFAULT'] + * + * // Optionally override the default user agent name. * userAgent: USER_AGENT * } * From ada3d04755c781ef9fd83a8e948ea25add999238 Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Mon, 7 Nov 2022 17:02:30 +0100 Subject: [PATCH 23/27] Fix notification filters config --- .../src/bolt/bolt-protocol-util.js | 2 +- .../src/bolt/bolt-protocol-v5x1.js | 2 +- .../src/bolt/request-message.js | 4 +- .../test/bolt/bolt-protocol-v5x1.test.js | 2 +- .../test/bolt/request-message.test.js | 126 +++++++++++++++--- packages/core/src/driver.ts | 4 +- .../bolt/bolt-protocol-util.js | 2 +- .../bolt/bolt-protocol-v5x1.js | 2 +- .../bolt-connection/bolt/request-message.js | 4 +- packages/neo4j-driver-deno/lib/core/driver.ts | 4 +- 10 files changed, 119 insertions(+), 33 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-util.js b/packages/bolt-connection/src/bolt/bolt-protocol-util.js index 7993435d1..d9c2997a9 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-util.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-util.js @@ -85,7 +85,7 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = * @param {any} observer */ function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { - if (notificationFilters != null) { + if (notificationFilters !== undefined) { const error = newError( 'Driver is connected to the database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js index abaa4199f..dc2bd29a8 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js @@ -179,7 +179,7 @@ function sanitizeNotificationFilters (filters) { } if (filters[0] === 'SERVER_DEFAULT') { - return undefined + return null } return filters.map(filter => filter.replace(/^ALL\./, '*.').replace(/\.ALL$/, '.*')) diff --git a/packages/bolt-connection/src/bolt/request-message.js b/packages/bolt-connection/src/bolt/request-message.js index 8099b4cd5..e4b712a49 100644 --- a/packages/bolt-connection/src/bolt/request-message.js +++ b/packages/bolt-connection/src/bolt/request-message.js @@ -136,7 +136,7 @@ export default class RequestMessage { extra.routing = routing } - if (notificationFilters) { + if (notificationFilters !== undefined) { extra.notifications = notificationFilters } @@ -330,7 +330,7 @@ function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser, if (impersonatedUser) { metadata.imp_user = assertString(impersonatedUser, 'impersonatedUser') } - if (notificationFilters) { + if (notificationFilters !== undefined) { metadata.notifications = notificationFilters } if (mode === ACCESS_MODE_READ) { 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 b2e825d6e..e74966e49 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -1124,7 +1124,7 @@ describe('#unit BoltProtocolV5x1', () => { return [ [['ALL.ALL', 'WARNING.ALL', 'ALL.HINT', 'INFORMATION.QUERY'], ['*.*', 'WARNING.*', '*.HINT', 'INFORMATION.QUERY']], [['NONE'], []], - [['SERVER_DEFAULT'], undefined] + [['SERVER_DEFAULT'], null] ] } }) diff --git a/packages/bolt-connection/test/bolt/request-message.test.js b/packages/bolt-connection/test/bolt/request-message.test.js index 7f6c35917..d56054926 100644 --- a/packages/bolt-connection/test/bolt/request-message.test.js +++ b/packages/bolt-connection/test/bolt/request-message.test.js @@ -448,22 +448,37 @@ describe('#unit RequestMessage', () => { ) }) - it('should create HELLO message without notification filters if it is not supplied or null', () => { - ;[null, undefined].forEach(notificationFilters => { - const userAgent = 'my-driver/1.0.2' - const authToken = { username: 'neo4j', password: 'secret' } + it('should create HELLO message with notification filters=null', () => { + const userAgent = 'my-driver/1.0.2' + const authToken = { username: 'neo4j', password: 'secret' } + const notificationFilters = null - const message = RequestMessage.hello5x1(authToken, { userAgent, notificationFilters }) + const message = RequestMessage.hello5x1(authToken, { userAgent, notificationFilters }) - expect(message.signature).toEqual(0x01) - expect(message.fields).toEqual([ - { username: 'neo4j', password: 'secret' }, - { user_agent: userAgent } - ]) - expect(message.toString()).toEqual( - `HELLO {...} {"user_agent":"${userAgent}"}` - ) - }) + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + { username: 'neo4j', password: 'secret' }, + { user_agent: userAgent, notifications: notificationFilters } + ]) + expect(message.toString()).toEqual( + `HELLO {...} {"user_agent":"${userAgent}","notifications":null}` + ) + }) + + it('should create HELLO message without notification filters if it is not supplied', () => { + const userAgent = 'my-driver/1.0.2' + const authToken = { username: 'neo4j', password: 'secret' } + + const message = RequestMessage.hello5x1(authToken, { userAgent, notificationFilters: undefined }) + + expect(message.signature).toEqual(0x01) + expect(message.fields).toEqual([ + { username: 'neo4j', password: 'secret' }, + { user_agent: userAgent } + ]) + expect(message.toString()).toEqual( + `HELLO {...} {"user_agent":"${userAgent}"}` + ) }) it('should create HELLO message with routing', () => { @@ -512,13 +527,13 @@ describe('#unit RequestMessage', () => { }) }) - it('should create BEGIN message without notification filters if it is not supplied or null', () => { - ;[undefined, null].forEach(notificationFilters => { + it('should create BEGIN message with notification filters=null', () => { + ;[READ, WRITE].forEach(mode => { const bookmarks = new Bookmarks([ 'neo4j:bookmark:v1:tx1', 'neo4j:bookmark:v1:tx10' ]) - const mode = WRITE + const notificationFilters = null const txConfig = new TxConfig({ timeout: 42, metadata: { key: 42 } }) const message = RequestMessage.begin({ bookmarks, txConfig, mode, notificationFilters }) @@ -526,7 +541,11 @@ describe('#unit RequestMessage', () => { const expectedMetadata = { bookmarks: bookmarks.values(), tx_timeout: int(42), - tx_metadata: { key: 42 } + tx_metadata: { key: 42 }, + notifications: notificationFilters + } + if (mode === READ) { + expectedMetadata.mode = 'r' } expect(message.signature).toEqual(0x11) @@ -537,6 +556,29 @@ describe('#unit RequestMessage', () => { }) }) + it('should create BEGIN message without notification filters if it is not supplied', () => { + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10' + ]) + const mode = WRITE + const txConfig = new TxConfig({ timeout: 42, metadata: { key: 42 } }) + + const message = RequestMessage.begin({ bookmarks, txConfig, mode, notificationFilters: undefined }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(42), + tx_metadata: { key: 42 } + } + + expect(message.signature).toEqual(0x11) + expect(message.fields).toEqual([expectedMetadata]) + expect(message.toString()).toEqual( + `BEGIN ${json.stringify(expectedMetadata)}` + ) + }) + it('should create RUN message with the notification filters', () => { ;[READ, WRITE].forEach(mode => { const query = 'RETURN $x' @@ -579,9 +621,8 @@ describe('#unit RequestMessage', () => { }) }) - it('should create RUN message without notification filters if it is not supplied or null', () => { - ;[undefined, null].forEach(notificationFilters => { - const mode = WRITE + it('should create RUN message with the notification filters = null', () => { + ;[READ, WRITE].forEach(mode => { const query = 'RETURN $x' const parameters = { x: 42 } const bookmarks = new Bookmarks([ @@ -593,6 +634,7 @@ describe('#unit RequestMessage', () => { timeout: 999, metadata: { a: 'a', b: 'b' } }) + const notificationFilters = null const message = RequestMessage.runWithMetadata(query, parameters, { bookmarks, @@ -604,7 +646,11 @@ describe('#unit RequestMessage', () => { const expectedMetadata = { bookmarks: bookmarks.values(), tx_timeout: int(999), - tx_metadata: { a: 'a', b: 'b' } + tx_metadata: { a: 'a', b: 'b' }, + notifications: notificationFilters + } + if (mode === READ) { + expectedMetadata.mode = 'r' } expect(message.signature).toEqual(0x10) @@ -616,5 +662,41 @@ describe('#unit RequestMessage', () => { ) }) }) + + it('should create RUN message without notification filters if it is not supplied', () => { + const mode = WRITE + const query = 'RETURN $x' + const parameters = { x: 42 } + const bookmarks = new Bookmarks([ + 'neo4j:bookmark:v1:tx1', + 'neo4j:bookmark:v1:tx10', + 'neo4j:bookmark:v1:tx100' + ]) + const txConfig = new TxConfig({ + timeout: 999, + metadata: { a: 'a', b: 'b' } + }) + + const message = RequestMessage.runWithMetadata(query, parameters, { + bookmarks, + txConfig, + mode, + notificationFilters: undefined + }) + + const expectedMetadata = { + bookmarks: bookmarks.values(), + tx_timeout: int(999), + tx_metadata: { a: 'a', b: 'b' } + } + + expect(message.signature).toEqual(0x10) + expect(message.fields).toEqual([query, parameters, expectedMetadata]) + expect(message.toString()).toEqual( + `RUN ${query} ${json.stringify(parameters)} ${json.stringify( + expectedMetadata + )}` + ) + }) }) }) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index ea53e3b7e..afe12f064 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -216,7 +216,9 @@ class SessionConfig { * // using default server configuration * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) - * // OR SIMPLY: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * + * // using default configured in the connection/driver configuration + * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) * * // Enable all notifications * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js index c85fc8bdd..8684535db 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js @@ -85,7 +85,7 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = * @param {any} observer */ function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { - if (notificationFilters != null) { + if (notificationFilters !== undefined) { const error = newError( 'Driver is connected to the database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + 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 c19f445ba..6ec334afe 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 @@ -179,7 +179,7 @@ function sanitizeNotificationFilters (filters) { } if (filters[0] === 'SERVER_DEFAULT') { - return undefined + return null } return filters.map(filter => filter.replace(/^ALL\./, '*.').replace(/\.ALL$/, '.*')) 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 2c2024139..acf7d1ac5 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 @@ -136,7 +136,7 @@ export default class RequestMessage { extra.routing = routing } - if (notificationFilters) { + if (notificationFilters !== undefined) { extra.notifications = notificationFilters } @@ -330,7 +330,7 @@ function buildTxMetadata (bookmarks, txConfig, database, mode, impersonatedUser, if (impersonatedUser) { metadata.imp_user = assertString(impersonatedUser, 'impersonatedUser') } - if (notificationFilters) { + if (notificationFilters !== undefined) { metadata.notifications = notificationFilters } if (mode === ACCESS_MODE_READ) { diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 108000c86..07e2117c7 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -216,7 +216,9 @@ class SessionConfig { * // using default server configuration * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) - * // OR SIMPLY: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * + * // using default configured in the connection/driver configuration + * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) * * // Enable all notifications * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) From d4d7259022ddc9e5ad8d8fb8baf69af524022d73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antonio=20Barc=C3=A9los?= Date: Tue, 8 Nov 2022 11:56:01 +0100 Subject: [PATCH 24/27] Apply suggestions from code review Co-authored-by: Robsdedude --- packages/bolt-connection/src/bolt/bolt-protocol-util.js | 2 +- .../bolt-connection/test/bolt/bolt-protocol-v5x1.test.js | 4 ++-- packages/core/src/driver.ts | 6 +++--- packages/core/src/notification-filter.ts | 3 ++- packages/core/src/result-summary.ts | 4 ++-- packages/core/test/result-summary.test.ts | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/bolt-connection/src/bolt/bolt-protocol-util.js b/packages/bolt-connection/src/bolt/bolt-protocol-util.js index d9c2997a9..e3458a9a4 100644 --- a/packages/bolt-connection/src/bolt/bolt-protocol-util.js +++ b/packages/bolt-connection/src/bolt/bolt-protocol-util.js @@ -87,7 +87,7 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { if (notificationFilters !== undefined) { const error = newError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${json.stringify(notificationFilters)}.` ) 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 e74966e49..65c466484 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -657,8 +657,8 @@ describe('#unit BoltProtocolV5x1', () => { 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.') + '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() diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index afe12f064..1e1d0b4c3 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -201,10 +201,10 @@ class SessionConfig { * * The filters are defined by "{@link NotificationSeverityLevel}.{@link NotificationCategory}" with the * exception of the "UNKNOWN" severity and category. - * "ALL" is added for possible value of category and severity filters. + * Additionally, "ALL" can be set as category, severity, or both. * - * Disabling the filters is done by setting this configuration to ["NONE"]. - * Using the default values is done by setting this configuration to ["SERVER_DEFAULT"]. + * Disabling the filters is done by setting this configuration to ['NONE']. + * Using the default values is done by setting this configuration to ['SERVER_DEFAULT']. * * Helper constants and methods are defined at {@link notificationFilter}. * diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index a43613da5..195745270 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -16,6 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { NotificationCategory, NotificationSeverityLevel } from './result-summary' type Combine = `${Enum1}${Separator}${Enum2}` @@ -31,7 +32,7 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter /** * - * Notifications filters used during the {@link Driver} and {@link Session} configuration. + * Notification filters used for configuring {@link Driver} and {@link Session}. * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index aa620331f..b5d537314 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -499,7 +499,7 @@ class Notification { /** * The raw severity * - * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} enumerated value. + * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} for an enumerated value. * * @type {string} * @public @@ -551,7 +551,7 @@ class Notification { this.rawSeverityLevel = notification.severity /** - * The severity level + * The category * * @type {NotificationCategory} * @public diff --git a/packages/core/test/result-summary.test.ts b/packages/core/test/result-summary.test.ts index 15d3d7ae3..de7f87e68 100644 --- a/packages/core/test/result-summary.test.ts +++ b/packages/core/test/result-summary.test.ts @@ -125,7 +125,7 @@ describe('notificationSeverityLevel', () => { } }) - it('should values be assignable to NotificationSeverityLevel', () => { + it('should have values assignable to NotificationSeverityLevel', () => { for (const [, value] of Object.entries(notificationSeverityLevel)) { const assignableValue: NotificationSeverityLevel = value expect(assignableValue).toBeDefined() From 51064065196af7b7b1c68f8de9bebcd35625947f Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 8 Nov 2022 12:56:52 +0100 Subject: [PATCH 25/27] Addressing PR comments --- .../bolt-connection/test/bolt/bolt-protocol-v1.test.js | 10 +++++----- .../bolt-connection/test/bolt/bolt-protocol-v2.test.js | 10 +++++----- .../bolt-connection/test/bolt/bolt-protocol-v3.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v4x0.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v4x1.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v4x2.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v4x3.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v4x4.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v5x0.test.js | 10 +++++----- .../test/bolt/bolt-protocol-v5x1.test.js | 4 ++-- packages/core/test/driver.test.ts | 2 ++ .../lib/bolt-connection/bolt/bolt-protocol-util.js | 2 +- packages/neo4j-driver-deno/lib/core/driver.ts | 6 +++--- .../neo4j-driver-deno/lib/core/notification-filter.ts | 3 ++- packages/neo4j-driver-deno/lib/core/result-summary.ts | 4 ++-- 15 files changed, 57 insertions(+), 54 deletions(-) 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 258cd993a..4d6490ac5 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v1.test.js @@ -336,7 +336,7 @@ describe('#unit BoltProtocolV1', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV1)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -344,7 +344,7 @@ describe('#unit BoltProtocolV1', () => { const protocol = new BoltProtocolV1(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -358,7 +358,7 @@ describe('#unit BoltProtocolV1', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -370,7 +370,7 @@ describe('#unit BoltProtocolV1', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -382,7 +382,7 @@ describe('#unit BoltProtocolV1', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 d8a50118e..aea0716b5 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v2.test.js @@ -113,7 +113,7 @@ describe('#unit BoltProtocolV2', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV2)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -121,7 +121,7 @@ describe('#unit BoltProtocolV2', () => { const protocol = new BoltProtocolV2(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -135,7 +135,7 @@ describe('#unit BoltProtocolV2', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -147,7 +147,7 @@ describe('#unit BoltProtocolV2', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -159,7 +159,7 @@ describe('#unit BoltProtocolV2', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 892a86671..e5458d8f1 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v3.test.js @@ -295,7 +295,7 @@ describe('#unit BoltProtocolV3', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV3)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -303,7 +303,7 @@ describe('#unit BoltProtocolV3', () => { const protocol = new BoltProtocolV3(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -317,7 +317,7 @@ describe('#unit BoltProtocolV3', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -329,7 +329,7 @@ describe('#unit BoltProtocolV3', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -341,7 +341,7 @@ describe('#unit BoltProtocolV3', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 12bbb0da5..37c5aad60 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x0.test.js @@ -213,7 +213,7 @@ describe('#unit BoltProtocolV4x0', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV4x0)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -221,7 +221,7 @@ describe('#unit BoltProtocolV4x0', () => { const protocol = new BoltProtocolV4x0(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -235,7 +235,7 @@ describe('#unit BoltProtocolV4x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -247,7 +247,7 @@ describe('#unit BoltProtocolV4x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -259,7 +259,7 @@ describe('#unit BoltProtocolV4x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 add96c3c1..e09ebe329 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x1.test.js @@ -87,7 +87,7 @@ describe('#unit BoltProtocolV4x1', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV4x1)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -95,7 +95,7 @@ describe('#unit BoltProtocolV4x1', () => { const protocol = new BoltProtocolV4x1(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -109,7 +109,7 @@ describe('#unit BoltProtocolV4x1', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -121,7 +121,7 @@ describe('#unit BoltProtocolV4x1', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -133,7 +133,7 @@ describe('#unit BoltProtocolV4x1', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 8e66caa34..f8cffa3e6 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x2.test.js @@ -87,7 +87,7 @@ describe('#unit BoltProtocolV4x2', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV4x2)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -95,7 +95,7 @@ describe('#unit BoltProtocolV4x2', () => { const protocol = new BoltProtocolV4x2(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -109,7 +109,7 @@ describe('#unit BoltProtocolV4x2', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -121,7 +121,7 @@ describe('#unit BoltProtocolV4x2', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -133,7 +133,7 @@ describe('#unit BoltProtocolV4x2', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 b12292b2a..c1b649c70 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x3.test.js @@ -301,7 +301,7 @@ describe('#unit BoltProtocolV4x3', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV4x3)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -309,7 +309,7 @@ describe('#unit BoltProtocolV4x3', () => { const protocol = new BoltProtocolV4x3(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -323,7 +323,7 @@ describe('#unit BoltProtocolV4x3', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -335,7 +335,7 @@ describe('#unit BoltProtocolV4x3', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -347,7 +347,7 @@ describe('#unit BoltProtocolV4x3', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 24728988e..25f8f3927 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v4x4.test.js @@ -1131,7 +1131,7 @@ describe('#unit BoltProtocolV4x4', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV4x4)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -1139,7 +1139,7 @@ describe('#unit BoltProtocolV4x4', () => { const protocol = new BoltProtocolV4x4(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -1153,7 +1153,7 @@ describe('#unit BoltProtocolV4x4', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -1165,7 +1165,7 @@ describe('#unit BoltProtocolV4x4', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -1177,7 +1177,7 @@ describe('#unit BoltProtocolV4x4', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 9b76f29c6..f35c9f45e 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x0.test.js @@ -1030,7 +1030,7 @@ describe('#unit BoltProtocolV5x0', () => { describe('Bolt v5.1', () => { /** - * @param {string[]} notificationFilters The impersonated user. + * @param {string[]} notificationFilters The notification filters. * @param {function(protocol: BoltProtocolV5x0)} fn */ function verifyNotificationFiltersNotSupportedError (notificationFilters, fn) { @@ -1038,7 +1038,7 @@ describe('#unit BoltProtocolV5x0', () => { const protocol = new BoltProtocolV5x0(recorder, null, false, undefined, undefined, () => {}) expect(() => fn(protocol)).toThrowError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${JSON.stringify(notificationFilters)}.` ) @@ -1052,7 +1052,7 @@ describe('#unit BoltProtocolV5x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyInitialize('test') + verifyInitialize(['test']) }) }) @@ -1064,7 +1064,7 @@ describe('#unit BoltProtocolV5x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyBeginTransaction('test') + verifyBeginTransaction(['test']) }) }) @@ -1076,7 +1076,7 @@ describe('#unit BoltProtocolV5x0', () => { } it('should throw error when notificationFilters is set', () => { - verifyRun('test') + verifyRun(['test']) }) }) }) 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 65c466484..652246dda 100644 --- a/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js +++ b/packages/bolt-connection/test/bolt/bolt-protocol-v5x1.test.js @@ -181,7 +181,7 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([false, true]) }) - it.each(notificationFiltersFixtures())('should run a with notification filters', + it.each(notificationFiltersFixtures())('should run a with notification filters [%s]', (notificationFilters, sentNotificationFilters) => { const database = 'testdb' const bookmarks = new Bookmarks([ @@ -283,7 +283,7 @@ describe('#unit BoltProtocolV5x1', () => { expect(protocol.flushes).toEqual([true]) }) - it.each(notificationFiltersFixtures())('should begin a transaction with notification filters', + it.each(notificationFiltersFixtures())('should begin a transaction with notification filters [%s]', (notificationFilters, sentNotificationFilters) => { const database = 'testdb' const bookmarks = new Bookmarks([ diff --git a/packages/core/test/driver.test.ts b/packages/core/test/driver.test.ts index 40faa6cb0..79dbeabaf 100644 --- a/packages/core/test/driver.test.ts +++ b/packages/core/test/driver.test.ts @@ -160,6 +160,7 @@ describe('Driver', () => { it.each([ [], undefined, + null, notificationFilter.disabled(), notificationFilter.serverDefault(), [notificationFilter.ALL.ALL, notificationFilter.INFORMATION.GENERIC], @@ -411,6 +412,7 @@ describe('Driver', () => { describe('when set config.notificationFilters', () => { it.each([ [], + null, undefined, notificationFilter.disabled(), notificationFilter.serverDefault(), diff --git a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js index 8684535db..6a74ed98f 100644 --- a/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js +++ b/packages/neo4j-driver-deno/lib/bolt-connection/bolt/bolt-protocol-util.js @@ -87,7 +87,7 @@ function assertImpersonatedUserIsEmpty (impersonatedUser, onProtocolError = () = function assertNotificationFiltersIsEmpty (notificationFilters, onProtocolError = () => {}, observer) { if (notificationFilters !== undefined) { const error = newError( - 'Driver is connected to the database that does not support user notification filters. ' + + 'Driver is connected to a database that does not support user notification filters. ' + 'Please upgrade to neo4j 5.3.0 or later in order to use this functionality. ' + `Trying to set notifications to ${json.stringify(notificationFilters)}.` ) diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 07e2117c7..d4de2d568 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -201,10 +201,10 @@ class SessionConfig { * * The filters are defined by "{@link NotificationSeverityLevel}.{@link NotificationCategory}" with the * exception of the "UNKNOWN" severity and category. - * "ALL" is added for possible value of category and severity filters. + * Additionally, "ALL" can be set as category, severity, or both. * - * Disabling the filters is done by setting this configuration to ["NONE"]. - * Using the default values is done by setting this configuration to ["SERVER_DEFAULT"]. + * Disabling the filters is done by setting this configuration to ['NONE']. + * Using the default values is done by setting this configuration to ['SERVER_DEFAULT']. * * Helper constants and methods are defined at {@link notificationFilter}. * diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index a8b1f67fe..868606552 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -16,6 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { NotificationCategory, NotificationSeverityLevel } from './result-summary.ts' type Combine = `${Enum1}${Separator}${Enum2}` @@ -31,7 +32,7 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter /** * - * Notifications filters used during the {@link Driver} and {@link Session} configuration. + * Notification filters used for configuring {@link Driver} and {@link Session}. * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index 8780a44c1..7e06436df 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -499,7 +499,7 @@ class Notification { /** * The raw severity * - * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} enumerated value. + * Use {@link Notification#rawSeverityLevel} for the raw value or {@link Notification#severityLevel} for an enumerated value. * * @type {string} * @public @@ -551,7 +551,7 @@ class Notification { this.rawSeverityLevel = notification.severity /** - * The severity level + * The category * * @type {NotificationCategory} * @public From c89afc64f6e01978a64924b9324320f794200f4e Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 8 Nov 2022 13:09:23 +0100 Subject: [PATCH 26/27] Address comments in the PR --- packages/core/src/driver.ts | 24 +++++++++---------- packages/neo4j-driver-deno/lib/core/driver.ts | 24 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index 1e1d0b4c3..d53476632 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -210,23 +210,23 @@ class SessionConfig { * * @example * // disabling notifications - * const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) + * const sessionWithoutNotifications = driver.session({ database: 'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) * // EQUIVALENT TO: const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: ["NONE"] }) * * // using default server configuration - * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) - * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) + * const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) + * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) * * // using default configured in the connection/driver configuration - * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j' }) * * // Enable all notifications - * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) - * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: ['ALL.ALL'] }) + * const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) + * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: ['ALL.ALL'] }) * - * // Configuring for any categories with severity "WARNING", - * // or any severity with category "QUERY" - * // or severity "INFORMATION" and category "PERFORMANCE". + * // Configuring for any categories with severity 'WARNING', + * // or any severity with category 'QUERY' + * // or severity 'INFORMATION' and category 'PERFORMANCE'. * const sessionFineConfigured = driver.session({ * database: 'neo4j', * notificationFilters: [ @@ -236,9 +236,9 @@ class SessionConfig { * ] * }) * - * // Configuring for any categories with severity "WARNING", - * // or any severity with category "QUERY" - * // or severity "INFORMATION" and category "PERFORMANCE". + * // Configuring for any categories with severity 'WARNING', + * // or any severity with category 'QUERY' + * // or severity 'INFORMATION' and category 'PERFORMANCE'. * // const sessionFineConfigured = driver.session({ * // database: 'neo4j', * // notificationFilters: [ diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index d4de2d568..8f347aef3 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -210,23 +210,23 @@ class SessionConfig { * * @example * // disabling notifications - * const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) + * const sessionWithoutNotifications = driver.session({ database: 'neo4j', notificationFilters: neo4j.notificationFilter.disabled() }) * // EQUIVALENT TO: const sessionWithoutNotifications = driver.session({ database:'neo4j', notificationFilters: ["NONE"] }) * * // using default server configuration - * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) - * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) + * const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j', notificationFilters: neo4j.notificationFilter.serverDefault() }) + * // EQUIVALENT TO: const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j', notificationFilters: ["SERVER_DEFAULT"] }) * * // using default configured in the connection/driver configuration - * const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' }) + * const sessionWithSeverDefaultNotifications = driver.session({ database: 'neo4j' }) * * // Enable all notifications - * const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) - * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: ['ALL.ALL'] }) + * const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] }) + * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: ['ALL.ALL'] }) * - * // Configuring for any categories with severity "WARNING", - * // or any severity with category "QUERY" - * // or severity "INFORMATION" and category "PERFORMANCE". + * // Configuring for any categories with severity 'WARNING', + * // or any severity with category 'QUERY' + * // or severity 'INFORMATION' and category 'PERFORMANCE'. * const sessionFineConfigured = driver.session({ * database: 'neo4j', * notificationFilters: [ @@ -236,9 +236,9 @@ class SessionConfig { * ] * }) * - * // Configuring for any categories with severity "WARNING", - * // or any severity with category "QUERY" - * // or severity "INFORMATION" and category "PERFORMANCE". + * // Configuring for any categories with severity 'WARNING', + * // or any severity with category 'QUERY' + * // or severity 'INFORMATION' and category 'PERFORMANCE'. * // const sessionFineConfigured = driver.session({ * // database: 'neo4j', * // notificationFilters: [ From c58ceb6b273eed8dad0e786cdeb606f87ab03c1f Mon Sep 17 00:00:00 2001 From: Antonio Barcelos Date: Tue, 8 Nov 2022 13:27:45 +0100 Subject: [PATCH 27/27] Remove QUERY from Category enum --- packages/core/src/driver.ts | 8 ++++---- packages/core/src/notification-filter.ts | 12 ++++-------- packages/core/src/result-summary.ts | 5 ++--- packages/core/test/driver.test.ts | 12 ++++++------ packages/core/test/result-summary.test.ts | 1 - packages/core/test/session.test.ts | 4 ++-- packages/core/test/transaction.test.ts | 2 +- packages/neo4j-driver-deno/lib/core/driver.ts | 8 ++++---- .../lib/core/notification-filter.ts | 10 +++------- .../neo4j-driver-deno/lib/core/result-summary.ts | 5 ++--- packages/neo4j-driver-lite/test/unit/index.test.ts | 1 - packages/neo4j-driver/test/types/index.test.ts | 8 -------- 12 files changed, 28 insertions(+), 48 deletions(-) diff --git a/packages/core/src/driver.ts b/packages/core/src/driver.ts index d53476632..e7abb7060 100644 --- a/packages/core/src/driver.ts +++ b/packages/core/src/driver.ts @@ -225,25 +225,25 @@ class SessionConfig { * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: ['ALL.ALL'] }) * * // Configuring for any categories with severity 'WARNING', - * // or any severity with category 'QUERY' + * // or any severity with category 'HINT' * // or severity 'INFORMATION' and category 'PERFORMANCE'. * const sessionFineConfigured = driver.session({ * database: 'neo4j', * notificationFilters: [ * neo4j.notificationFilter.WARNING.ALL, - * neo4j.notificationFilter.ALL.QUERY, + * neo4j.notificationFilter.ALL.HINT, * neo4j.notificationFilter.INFORMATION.PERFORMANCE * ] * }) * * // Configuring for any categories with severity 'WARNING', - * // or any severity with category 'QUERY' + * // or any severity with category 'HINT' * // or severity 'INFORMATION' and category 'PERFORMANCE'. * // const sessionFineConfigured = driver.session({ * // database: 'neo4j', * // notificationFilters: [ * // 'WARNING.ALL', - * // 'ALL.QUERY', + * // 'ALL.HINT', * // 'INFORMATION.PERFORMANCE' * // ] * // }) diff --git a/packages/core/src/notification-filter.ts b/packages/core/src/notification-filter.ts index 195745270..eac67e2d3 100644 --- a/packages/core/src/notification-filter.ts +++ b/packages/core/src/notification-filter.ts @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + import { NotificationCategory, NotificationSeverityLevel } from './result-summary' type Combine = `${Enum1}${Separator}${Enum2}` @@ -36,11 +36,11 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | - * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + * 'ALL.PERFORMANCE' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | * 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | - * 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + * 'INFORMATION.PERFORMANCE' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | - * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter + * 'WARNING.PERFORMANCE' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter */ /** * Defines the category filters available for a given severity level filter @@ -51,7 +51,6 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter * @property {NotificationFilter} GENERIC * @property {NotificationFilter} HINT * @property {NotificationFilter} PERFORMANCE - * @property {NotificationFilter} QUERY * @property {NotificationFilter} UNRECOGNIZED * @property {NotificationFilter} UNSUPPORTED */ @@ -76,7 +75,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'ALL.GENERIC', HINT: 'ALL.HINT', PERFORMANCE: 'ALL.PERFORMANCE', - QUERY: 'ALL.QUERY', UNRECOGNIZED: 'ALL.UNRECOGNIZED', UNSUPPORTED: 'ALL.UNSUPPORTED' }, @@ -86,7 +84,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'INFORMATION.GENERIC', HINT: 'INFORMATION.HINT', PERFORMANCE: 'INFORMATION.PERFORMANCE', - QUERY: 'INFORMATION.QUERY', UNRECOGNIZED: 'INFORMATION.UNRECOGNIZED', UNSUPPORTED: 'INFORMATION.UNSUPPORTED' }, @@ -96,7 +93,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'WARNING.GENERIC', HINT: 'WARNING.HINT', PERFORMANCE: 'WARNING.PERFORMANCE', - QUERY: 'WARNING.QUERY', UNRECOGNIZED: 'WARNING.UNRECOGNIZED', UNSUPPORTED: 'WARNING.UNSUPPORTED' } diff --git a/packages/core/src/result-summary.ts b/packages/core/src/result-summary.ts index b5d537314..ed2ec6708 100644 --- a/packages/core/src/result-summary.ts +++ b/packages/core/src/result-summary.ts @@ -435,17 +435,16 @@ const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { Object.freeze(notificationSeverityLevel) const severityLevels = Object.values(notificationSeverityLevel) -type NotificationCategory = 'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | +type NotificationCategory = 'HINT' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'GENERIC' | 'UNKNOWN' /** - * @typedef {'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + * @typedef {'HINT' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory */ /** * Constants that represents the Category in the {@link Notification} */ const notificationCategory: { [key in NotificationCategory]: key } = { HINT: 'HINT', - QUERY: 'QUERY', UNRECOGNIZED: 'UNRECOGNIZED', UNSUPPORTED: 'UNSUPPORTED', PERFORMANCE: 'PERFORMANCE', diff --git a/packages/core/test/driver.test.ts b/packages/core/test/driver.test.ts index 79dbeabaf..32b90815c 100644 --- a/packages/core/test/driver.test.ts +++ b/packages/core/test/driver.test.ts @@ -164,7 +164,7 @@ describe('Driver', () => { notificationFilter.disabled(), notificationFilter.serverDefault(), [notificationFilter.ALL.ALL, notificationFilter.INFORMATION.GENERIC], - ['WARNING.QUERY', 'INFORMATION.GENERIC'] + ['WARNING.HINT', 'INFORMATION.GENERIC'] ])('should send valid "notificationFilters" to the session', async (notificationFilters?: NotificationFilter[]) => { const driver = new Driver( META_INFO, @@ -187,10 +187,10 @@ describe('Driver', () => { it.each([ notificationFilter.ALL.DEPRECATION, - 'WARNING.QUERY', + 'WARNING.HINT', 'INFO', 1234, - { 'WARNING.QUERY': notificationFilter.WARNING.QUERY }, + { 'WARNING.HINT': notificationFilter.WARNING.HINT }, () => [notificationFilter.ALL.DEPRECATION] ])('should thrown when "notificationFilters" is not an array', async (notificationFilters?: any) => { const driver = new Driver( @@ -417,7 +417,7 @@ describe('Driver', () => { notificationFilter.disabled(), notificationFilter.serverDefault(), [notificationFilter.ALL.ALL, notificationFilter.INFORMATION.GENERIC], - ['WARNING.QUERY', 'INFORMATION.GENERIC'] + ['WARNING.HINT', 'INFORMATION.GENERIC'] ])('should send valid "notificationFilters" to the connection provider', async (notificationFilters?: NotificationFilter[]) => { const createConnectionProviderMock = jest.fn(mockCreateConnectonProvider(connectionProvider)) const driver = new Driver( @@ -441,10 +441,10 @@ describe('Driver', () => { it.each([ notificationFilter.ALL.DEPRECATION, - 'WARNING.QUERY', + 'WARNING.HINT', 'INFO', 1234, - { 'WARNING.QUERY': notificationFilter.WARNING.QUERY }, + { 'WARNING.HINT': notificationFilter.WARNING.HINT }, () => [notificationFilter.ALL.DEPRECATION] ])('should thrown when "notificationFilters" is not an array', async (notificationFilters?: any) => { const createConnectionProviderMock = mockCreateConnectonProvider(connectionProvider) diff --git a/packages/core/test/result-summary.test.ts b/packages/core/test/result-summary.test.ts index de7f87e68..20b6fd77a 100644 --- a/packages/core/test/result-summary.test.ts +++ b/packages/core/test/result-summary.test.ts @@ -169,7 +169,6 @@ function getValidSeverityLevels (): NotificationSeverityLevel[] { function getValidCategories (): NotificationCategory[] { return [ 'HINT', - 'QUERY', 'UNRECOGNIZED', 'UNSUPPORTED', 'PERFORMANCE', diff --git a/packages/core/test/session.test.ts b/packages/core/test/session.test.ts index 1364e7231..c67d2d192 100644 --- a/packages/core/test/session.test.ts +++ b/packages/core/test/session.test.ts @@ -451,7 +451,7 @@ describe('session', () => { undefined, [], [notificationFilter.ALL.ALL], - [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.HINT'] ])('should call run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { const connection = mockBeginWithSuccess(newFakeConnection()) @@ -868,7 +868,7 @@ describe('session', () => { undefined, [], [notificationFilter.ALL.ALL], - [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.HINT'] ])('should call run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { const connection = newFakeConnection() diff --git a/packages/core/test/transaction.test.ts b/packages/core/test/transaction.test.ts index 1b9005323..358f8a210 100644 --- a/packages/core/test/transaction.test.ts +++ b/packages/core/test/transaction.test.ts @@ -380,7 +380,7 @@ function testTx (transactionName: string, newTransaction: undefined, [], [notificationFilter.ALL.ALL], - [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.QUERY'] + [notificationFilter.INFORMATION.DEPRECATION, 'WARNING.HINT'] ])('should call not run query with notificationFilters', async (notificationFilters: NotificationFilter[]) => { const connection = newFakeConnection() const tx = newTransaction({ diff --git a/packages/neo4j-driver-deno/lib/core/driver.ts b/packages/neo4j-driver-deno/lib/core/driver.ts index 8f347aef3..5b8095510 100644 --- a/packages/neo4j-driver-deno/lib/core/driver.ts +++ b/packages/neo4j-driver-deno/lib/core/driver.ts @@ -225,25 +225,25 @@ class SessionConfig { * // EQUIVALENT TO: const sessionWithAllNotifications = driver.session({ database: 'neo4j', notificationFilters: ['ALL.ALL'] }) * * // Configuring for any categories with severity 'WARNING', - * // or any severity with category 'QUERY' + * // or any severity with category 'HINT' * // or severity 'INFORMATION' and category 'PERFORMANCE'. * const sessionFineConfigured = driver.session({ * database: 'neo4j', * notificationFilters: [ * neo4j.notificationFilter.WARNING.ALL, - * neo4j.notificationFilter.ALL.QUERY, + * neo4j.notificationFilter.ALL.HINT, * neo4j.notificationFilter.INFORMATION.PERFORMANCE * ] * }) * * // Configuring for any categories with severity 'WARNING', - * // or any severity with category 'QUERY' + * // or any severity with category 'HINT' * // or severity 'INFORMATION' and category 'PERFORMANCE'. * // const sessionFineConfigured = driver.session({ * // database: 'neo4j', * // notificationFilters: [ * // 'WARNING.ALL', - * // 'ALL.QUERY', + * // 'ALL.HINT', * // 'INFORMATION.PERFORMANCE' * // ] * // }) diff --git a/packages/neo4j-driver-deno/lib/core/notification-filter.ts b/packages/neo4j-driver-deno/lib/core/notification-filter.ts index 868606552..5ed38e1ae 100644 --- a/packages/neo4j-driver-deno/lib/core/notification-filter.ts +++ b/packages/neo4j-driver-deno/lib/core/notification-filter.ts @@ -36,11 +36,11 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter * * @typedef { 'NONE' | 'SERVER_DEFAULT' | * 'ALL.ALL' | 'ALL.DEPRECATION' | 'ALL.GENERIC' | 'ALL.HINT' | - * 'ALL.PERFORMANCE' | 'ALL.QUERY' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | + * 'ALL.PERFORMANCE' | 'ALL.UNRECOGNIZED' | 'ALL.UNSUPPORTED' | * 'INFORMATION.ALL' | 'INFORMATION.DEPRECATION' | 'INFORMATION.GENERIC' | 'INFORMATION.HINT' | - * 'INFORMATION.PERFORMANCE' | 'INFORMATION.QUERY' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | + * 'INFORMATION.PERFORMANCE' | 'INFORMATION.UNRECOGNIZED' | 'INFORMATION.UNSUPPORTED' | * 'WARNING.ALL' | 'WARNING.DEPRECATION' | 'WARNING.GENERIC' | 'WARNING.HINT' | - * 'WARNING.PERFORMANCE' | 'WARNING.QUERY' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter + * 'WARNING.PERFORMANCE' | 'WARNING.UNRECOGNIZED' | 'WARNING.UNSUPPORTED' } NotificationFilter */ /** * Defines the category filters available for a given severity level filter @@ -51,7 +51,6 @@ type NotificationFilter = 'NONE' | 'SERVER_DEFAULT' | CombinedFilter * @property {NotificationFilter} GENERIC * @property {NotificationFilter} HINT * @property {NotificationFilter} PERFORMANCE - * @property {NotificationFilter} QUERY * @property {NotificationFilter} UNRECOGNIZED * @property {NotificationFilter} UNSUPPORTED */ @@ -76,7 +75,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'ALL.GENERIC', HINT: 'ALL.HINT', PERFORMANCE: 'ALL.PERFORMANCE', - QUERY: 'ALL.QUERY', UNRECOGNIZED: 'ALL.UNRECOGNIZED', UNSUPPORTED: 'ALL.UNSUPPORTED' }, @@ -86,7 +84,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'INFORMATION.GENERIC', HINT: 'INFORMATION.HINT', PERFORMANCE: 'INFORMATION.PERFORMANCE', - QUERY: 'INFORMATION.QUERY', UNRECOGNIZED: 'INFORMATION.UNRECOGNIZED', UNSUPPORTED: 'INFORMATION.UNSUPPORTED' }, @@ -96,7 +93,6 @@ const notificationFilter: SeverityDotCategoryFilters & { GENERIC: 'WARNING.GENERIC', HINT: 'WARNING.HINT', PERFORMANCE: 'WARNING.PERFORMANCE', - QUERY: 'WARNING.QUERY', UNRECOGNIZED: 'WARNING.UNRECOGNIZED', UNSUPPORTED: 'WARNING.UNSUPPORTED' } diff --git a/packages/neo4j-driver-deno/lib/core/result-summary.ts b/packages/neo4j-driver-deno/lib/core/result-summary.ts index 7e06436df..700f83631 100644 --- a/packages/neo4j-driver-deno/lib/core/result-summary.ts +++ b/packages/neo4j-driver-deno/lib/core/result-summary.ts @@ -435,17 +435,16 @@ const notificationSeverityLevel: { [key in NotificationSeverityLevel]: key } = { Object.freeze(notificationSeverityLevel) const severityLevels = Object.values(notificationSeverityLevel) -type NotificationCategory = 'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | +type NotificationCategory = 'HINT' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'GENERIC' | 'UNKNOWN' /** - * @typedef {'HINT' | 'QUERY' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory + * @typedef {'HINT' | 'UNRECOGNIZED' | 'UNSUPPORTED' |'PERFORMANCE' | 'DEPRECATION' | 'RUNTIME' | 'UNKNOWN'} NotificationCategory */ /** * Constants that represents the Category in the {@link Notification} */ const notificationCategory: { [key in NotificationCategory]: key } = { HINT: 'HINT', - QUERY: 'QUERY', UNRECOGNIZED: 'UNRECOGNIZED', UNSUPPORTED: 'UNSUPPORTED', PERFORMANCE: 'PERFORMANCE', diff --git a/packages/neo4j-driver-lite/test/unit/index.test.ts b/packages/neo4j-driver-lite/test/unit/index.test.ts index 3b1cb5a77..b06034dcc 100644 --- a/packages/neo4j-driver-lite/test/unit/index.test.ts +++ b/packages/neo4j-driver-lite/test/unit/index.test.ts @@ -318,7 +318,6 @@ describe('index', () => { it('should export notificationCategory', () => { expect(neo4j.notificationCategory).toBeDefined() expect(neo4j.notificationCategory.HINT).toBeDefined() - expect(neo4j.notificationCategory.QUERY).toBeDefined() expect(neo4j.notificationCategory.UNRECOGNIZED).toBeDefined() expect(neo4j.notificationCategory.UNSUPPORTED).toBeDefined() expect(neo4j.notificationCategory.PERFORMANCE).toBeDefined() diff --git a/packages/neo4j-driver/test/types/index.test.ts b/packages/neo4j-driver/test/types/index.test.ts index 11e0f5c18..401042e1d 100644 --- a/packages/neo4j-driver/test/types/index.test.ts +++ b/packages/neo4j-driver/test/types/index.test.ts @@ -103,7 +103,6 @@ const informationSeverity: NotificationSeverityLevel = notificationSeverityLevel const hintCategoryString: string = notificationCategory.HINT const deprecationCategoryString: string = notificationCategory.DEPRECATION const performanceCategoryString: string = notificationCategory.PERFORMANCE -const queryCategoryString: string = notificationCategory.QUERY const genericCategoryString: string = notificationCategory.GENERIC const unrecognizedCategoryString: string = notificationCategory.UNRECOGNIZED const unsupportedCategoryString: string = notificationCategory.UNSUPPORTED @@ -111,7 +110,6 @@ const unknownCategoryString: string = notificationCategory.UNKNOWN const hintCategory: NotificationCategory = notificationCategory.HINT const deprecationCategory: NotificationCategory = notificationCategory.DEPRECATION const performanceCategory: NotificationCategory = notificationCategory.PERFORMANCE -const queryCategory: NotificationCategory = notificationCategory.QUERY const genericCategory: NotificationCategory = notificationCategory.GENERIC const unrecognizedCategory: NotificationCategory = notificationCategory.UNRECOGNIZED const unsupportedCategory: NotificationCategory = notificationCategory.UNSUPPORTED @@ -123,7 +121,6 @@ const allPerformanceString: string = notificationFilter.ALL.PERFORMANCE const allGenericString: string = notificationFilter.ALL.GENERIC const allUnrecognizedString: string = notificationFilter.ALL.UNRECOGNIZED const allUnsupportedString: string = notificationFilter.ALL.UNSUPPORTED -const allQueryString: string = notificationFilter.ALL.QUERY const allHintString: string = notificationFilter.ALL.HINT const allAllFilter: NotificationFilter = notificationFilter.ALL.ALL @@ -132,7 +129,6 @@ const allPerformanceFilter: NotificationFilter = notificationFilter.ALL.PERFORMA const allGenericFilter: NotificationFilter = notificationFilter.ALL.GENERIC const allUnrecognizedFilter: NotificationFilter = notificationFilter.ALL.UNRECOGNIZED const allUnsupportedFilter: NotificationFilter = notificationFilter.ALL.UNSUPPORTED -const allQueryFilter: NotificationFilter = notificationFilter.ALL.QUERY const allHintFilter: NotificationFilter = notificationFilter.ALL.HINT const informationAllString: string = notificationFilter.INFORMATION.ALL @@ -141,7 +137,6 @@ const informationPerformanceString: string = notificationFilter.INFORMATION.PERF const informationGenericString: string = notificationFilter.INFORMATION.GENERIC const informationUnrecognizedString: string = notificationFilter.INFORMATION.UNRECOGNIZED const informationUnsupportedString: string = notificationFilter.INFORMATION.UNSUPPORTED -const informationQueryString: string = notificationFilter.INFORMATION.QUERY const informationHintString: string = notificationFilter.INFORMATION.HINT const informationAllFilter: NotificationFilter = notificationFilter.INFORMATION.ALL @@ -150,7 +145,6 @@ const informationPerformanceFilter: NotificationFilter = notificationFilter.INFO const informationGenericFilter: NotificationFilter = notificationFilter.INFORMATION.GENERIC const informationUnrecognizedFilter: NotificationFilter = notificationFilter.INFORMATION.UNRECOGNIZED const informationUnsupportedFilter: NotificationFilter = notificationFilter.INFORMATION.UNSUPPORTED -const informationQueryFilter: NotificationFilter = notificationFilter.INFORMATION.QUERY const informationHintFilter: NotificationFilter = notificationFilter.INFORMATION.HINT const warningAllString: string = notificationFilter.WARNING.ALL @@ -159,7 +153,6 @@ const warningPerformanceString: string = notificationFilter.WARNING.PERFORMANCE const warningGenericString: string = notificationFilter.WARNING.GENERIC const warningUnrecognizedString: string = notificationFilter.WARNING.UNRECOGNIZED const warningUnsupportedString: string = notificationFilter.WARNING.UNSUPPORTED -const warningQueryString: string = notificationFilter.WARNING.QUERY const warningHintString: string = notificationFilter.WARNING.HINT const warningAllFilter: NotificationFilter = notificationFilter.WARNING.ALL @@ -168,5 +161,4 @@ const warningPerformanceFilter: NotificationFilter = notificationFilter.WARNING. const warningGenericFilter: NotificationFilter = notificationFilter.WARNING.GENERIC const warningUnrecognizedFilter: NotificationFilter = notificationFilter.WARNING.UNRECOGNIZED const warningUnsupportedFilter: NotificationFilter = notificationFilter.WARNING.UNSUPPORTED -const warningQueryFilter: NotificationFilter = notificationFilter.WARNING.QUERY const warningHintFilter: NotificationFilter = notificationFilter.WARNING.HINT