Skip to content

Database Notification Improvements #1008

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3d9d59a
Database Notification Improvements
bigmontz Oct 19, 2022
d1cac6e
Add tests for Notification, notificationSeverityLevel and notificatio…
bigmontz Oct 19, 2022
eec82a8
Assert not sending notification filters the current implementations o…
bigmontz Oct 20, 2022
bac205a
Implement notification filters validation from v1 to v5.0
bigmontz Oct 26, 2022
9fd8e45
Add BoltProtocolV5x1 with notification filters
bigmontz Oct 26, 2022
c2e9bcb
Add bolt 5.1 to handshake
bigmontz Oct 26, 2022
3899d8d
Add notification filters to Connection.initialize
bigmontz Oct 26, 2022
ef4320c
Add notification configuration to session and driver
bigmontz Oct 27, 2022
0d255e7
testkit
bigmontz Oct 27, 2022
8b6dcf3
Adjust categories
bigmontz Nov 1, 2022
49d070b
Update HELLO message for 5.1
bigmontz Nov 1, 2022
7594988
Small adjustment
bigmontz Nov 1, 2022
73b86af
Fix features name
bigmontz Nov 1, 2022
03d6109
Add NotificationFilter and notificationFilter to core
bigmontz Nov 3, 2022
24bbe3f
Export NotificationFilter and notificationFilter
bigmontz Nov 3, 2022
33887c9
Sanitize notification filters
bigmontz Nov 3, 2022
2dd6816
Validate notification filters while create sessions and drivers
bigmontz Nov 3, 2022
cdfe17e
Add tests to verify filters are being send to the protocol
bigmontz Nov 3, 2022
1152f59
improve filters documentation
bigmontz Nov 3, 2022
8e7cb10
Improve notification filter definition by deriving from category and …
bigmontz Nov 4, 2022
3fa1439
Adjust deno code
bigmontz Nov 4, 2022
362b141
Add configuration docs
bigmontz Nov 4, 2022
ada3d04
Fix notification filters config
bigmontz Nov 7, 2022
d4d7259
Apply suggestions from code review
bigmontz Nov 8, 2022
5106406
Addressing PR comments
bigmontz Nov 8, 2022
c89afc6
Address comments in the PR
bigmontz Nov 8, 2022
c58ceb6
Remove QUERY from Category enum
bigmontz Nov 8, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/bolt-connection/src/bolt/bolt-protocol-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not yet clear if this feature will make it into 5.3 or later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will hold this comment/pr until we have the functionality merged in the server and we know which version it will be part of.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 just make sure to not forget to update the parts of the PR that mention the version (also some doc comments).

Expand Down
2 changes: 1 addition & 1 deletion packages/bolt-connection/src/bolt/bolt-protocol-v5x1.js
Original file line number Diff line number Diff line change
Expand Up @@ -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$/, '.*'))
Expand Down
4 changes: 2 additions & 2 deletions packages/bolt-connection/src/bolt/request-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class RequestMessage {
extra.routing = routing
}

if (notificationFilters) {
if (notificationFilters !== undefined) {
extra.notifications = notificationFilters
}

Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
]
}
})
126 changes: 104 additions & 22 deletions packages/bolt-connection/test/bolt/request-message.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -512,21 +527,25 @@ 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 })

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)
Expand All @@ -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'
Expand Down Expand Up @@ -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([
Expand All @@ -593,6 +634,7 @@ describe('#unit RequestMessage', () => {
timeout: 999,
metadata: { a: 'a', b: 'b' }
})
const notificationFilters = null

const message = RequestMessage.runWithMetadata(query, parameters, {
bookmarks,
Expand All @@ -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)
Expand All @@ -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
)}`
)
})
})
})
4 changes: 3 additions & 1 deletion packages/core/src/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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] })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. ' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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$/, '.*'))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class RequestMessage {
extra.routing = routing
}

if (notificationFilters) {
if (notificationFilters !== undefined) {
extra.notifications = notificationFilters
}

Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 3 additions & 1 deletion packages/neo4j-driver-deno/lib/core/driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* // using default configured in the connection/driver configuration
* // using whatever filters were configured for the {@link Driver} (default behavior)

* const sessionWithSeverDefaultNotifications = driver.session({ database:'neo4j' })
*
* // Enable all notifications
* const sessionWithAllNotifications = driver.session({ database:'neo4j', notificationFilters: [neo4j.notificationFilter.ALL.ALL] })
Expand Down