Skip to content

Commit a621f58

Browse files
committed
Fix Driver.onCompleted callback
Setting this callback will now trigger connectivity verification. Provided callback function will be invoked if verification succeeds. When `onCompleted` callback is not set no verification will happen. This is achieved using getter/setter on `onCompleted` property. This callback has previously been broken when session was decoupled from connection. Removed `Driver.onCompleted` and `Driver.onError` from README. These callbacks were introduced specifically for neo4j-browser that needs to verify connectivity and be notified about broken WebSockets. So they were always a "special" not fully thought through part of driver's public API. It is preferable to do connectivity verification by running a dummy query like `RETURN 1`. Note that it's not always required.
1 parent 56941b8 commit a621f58

File tree

11 files changed

+162
-95
lines changed

11 files changed

+162
-95
lines changed

README.md

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,12 @@ driver.close();
6161

6262
## Usage examples
6363

64-
Driver creation:
64+
Driver lifecycle:
6565
```javascript
6666
// Create a driver instance, for the user neo4j with password neo4j.
6767
// It should be enough to have a single driver per database per application.
6868
var driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"));
6969

70-
// Register a callback to know if driver creation was successful:
71-
driver.onCompleted = function () {
72-
// proceed with using the driver, it was successfully instantiated
73-
};
74-
75-
// Register a callback to know if driver creation failed.
76-
// This could happen due to wrong credentials or database unavailability:
77-
driver.onError = function (error) {
78-
console.log('Driver instantiation failed', error);
79-
};
80-
8170
// Close the driver when application exits.
8271
// This closes all used network connections.
8372
driver.close();

src/v1/driver.js

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import StreamObserver from './internal/stream-observer';
2424
import {newError, SERVICE_UNAVAILABLE} from './error';
2525
import {DirectConnectionProvider} from './internal/connection-providers';
2626
import Bookmark from './internal/bookmark';
27+
import ConnectivityVerifier from './internal/connectivity-verifier';
2728

2829
const READ = 'READ', WRITE = 'WRITE';
2930
/**
@@ -72,6 +73,21 @@ class Driver {
7273
* @protected
7374
*/
7475
this._connectionProvider = null;
76+
77+
this._onCompleted = null;
78+
}
79+
80+
get onCompleted() {
81+
return this._onCompleted;
82+
}
83+
84+
set onCompleted(callback) {
85+
this._onCompleted = callback;
86+
if (this._onCompleted) {
87+
const connectionProvider = this._getOrCreateConnectionProvider();
88+
const connectivityVerifier = new ConnectivityVerifier(connectionProvider, this._onCompleted);
89+
connectivityVerifier.verify();
90+
}
7591
}
7692

7793
/**
@@ -219,16 +235,6 @@ class _ConnectionStreamObserver extends StreamObserver {
219235
this._hasFailed = true;
220236
}
221237
}
222-
223-
onCompleted(message) {
224-
if (this._driver.onCompleted) {
225-
this._driver.onCompleted(message);
226-
}
227-
228-
if (this._observer && this._observer.onComplete) {
229-
this._observer.onCompleted(message);
230-
}
231-
}
232238
}
233239

234240
/**
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright (c) 2002-2017 "Neo Technology,","
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
import ConnectionHolder from './connection-holder';
21+
import {READ} from '../driver';
22+
import StreamObserver from './stream-observer';
23+
24+
/**
25+
* Verifies connectivity using the given connection provider.
26+
*/
27+
export default class ConnectivityVerifier {
28+
29+
/**
30+
* @constructor
31+
* @param {ConnectionProvider} connectionProvider the provider to obtain connections from.
32+
* @param {function} successCallback a callback to invoke when verification succeeds.
33+
*/
34+
constructor(connectionProvider, successCallback) {
35+
this._connectionProvider = connectionProvider;
36+
this._successCallback = successCallback;
37+
}
38+
39+
verify() {
40+
acquireAndReleaseDummyConnection(this._connectionProvider).then(serverInfo => {
41+
if (this._successCallback) {
42+
this._successCallback(serverInfo);
43+
}
44+
}).catch(ignoredError => {
45+
});
46+
}
47+
}
48+
49+
/**
50+
* @private
51+
* @param {ConnectionProvider} connectionProvider the provider to obtain connections from.
52+
* @return {Promise<object>} promise resolved with server info or rejected with error.
53+
*/
54+
function acquireAndReleaseDummyConnection(connectionProvider) {
55+
const connectionHolder = new ConnectionHolder(READ, connectionProvider);
56+
connectionHolder.initializeConnection();
57+
const dummyObserver = new StreamObserver();
58+
const connectionPromise = connectionHolder.getConnection(dummyObserver);
59+
60+
return connectionPromise.then(connection => {
61+
// able to establish a connection
62+
return connectionHolder.close().then(() => connection.server);
63+
}).catch(error => {
64+
// failed to establish a connection
65+
return connectionHolder.close().catch(ignoredError => {
66+
// ignore connection release error
67+
}).then(() => {
68+
return Promise.reject(error);
69+
});
70+
});
71+
}

src/v1/internal/server-version.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,12 @@ function compareInts(x, y) {
9191
return (x < y) ? -1 : ((x === y) ? 0 : 1);
9292
}
9393

94+
const VERSION_3_1_0 = new ServerVersion(3, 1, 0);
9495
const VERSION_3_2_0 = new ServerVersion(3, 2, 0);
9596

9697
export {
9798
ServerVersion,
99+
VERSION_3_1_0,
98100
VERSION_3_2_0
99101
};
100102

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Copyright (c) 2002-2017 "Neo Technology,","
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
import ConnectivityVerifier from '../../src/v1/internal/connectivity-verifier';
21+
import {SingleConnectionProvider} from '../../src/v1/internal/connection-providers';
22+
import FakeConnection from './fake-connection';
23+
24+
describe('ConnectivityVerifier', () => {
25+
26+
it('should call success callback when able to acquire and release a connection', done => {
27+
const connectionPromise = Promise.resolve(new FakeConnection());
28+
const connectionProvider = new SingleConnectionProvider(connectionPromise);
29+
30+
const verifier = new ConnectivityVerifier(connectionProvider, () => {
31+
done();
32+
});
33+
34+
verifier.verify();
35+
});
36+
37+
});

test/types/v1/driver.test.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import Driver, {
3030
import {Parameters} from "../../../types/v1/statement-runner";
3131
import Session from "../../../types/v1/session";
3232
import {Neo4jError} from "../../../types/v1/error";
33+
import {ServerInfo} from "../../../types/v1/result-summary";
3334

3435
const dummy: any = null;
3536

@@ -87,11 +88,12 @@ session1.run("RETURN 1").then(result => {
8788

8889
const close: void = driver.close();
8990

90-
driver.onCompleted = (metadata: { server: string }) => {
91-
console.log(metadata.server);
91+
driver.onCompleted = (serverInfo: ServerInfo) => {
92+
console.log(serverInfo.version);
93+
console.log(serverInfo.address);
9294
};
9395

94-
driver.onCompleted({server: "Neo4j/3.2.0"});
96+
driver.onCompleted({version: "Neo4j/3.2.0", address: "localhost:7687"});
9597

9698
driver.onError = (error: Neo4jError) => {
9799
console.log(error);

test/v1/driver.test.js

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -112,51 +112,43 @@ describe('driver', () => {
112112
driver = neo4j.driver("bolt://localhost", sharedNeo4j.authToken);
113113

114114
// Expect
115-
driver.onCompleted = meta => {
115+
driver.onCompleted = server => {
116+
expect(server.address).toBeDefined();
116117
done();
117118
};
118-
119-
// When
120-
startNewTransaction(driver);
121119
});
122120

123121
it('should be possible to pass a realm with basic auth tokens', done => {
124122
// Given
125123
driver = neo4j.driver("bolt://localhost", neo4j.auth.basic(sharedNeo4j.username, sharedNeo4j.password, "native"));
126124

127125
// Expect
128-
driver.onCompleted = meta => {
126+
driver.onCompleted = server => {
127+
expect(server.address).toBeDefined();
129128
done();
130129
};
131-
132-
// When
133-
startNewTransaction(driver);
134130
});
135131

136132
it('should be possible to create custom auth tokens', done => {
137133
// Given
138134
driver = neo4j.driver("bolt://localhost", neo4j.auth.custom(sharedNeo4j.username, sharedNeo4j.password, "native", "basic"));
139135

140136
// Expect
141-
driver.onCompleted = meta => {
137+
driver.onCompleted = server => {
138+
expect(server.address).toBeDefined();
142139
done();
143140
};
144-
145-
// When
146-
startNewTransaction(driver);
147141
});
148142

149143
it('should be possible to create custom auth tokens with additional parameters', done => {
150144
// Given
151145
driver = neo4j.driver("bolt://localhost", neo4j.auth.custom(sharedNeo4j.username, sharedNeo4j.password, "native", "basic", {secret: 42}));
152146

153147
// Expect
154-
driver.onCompleted = () => {
148+
driver.onCompleted = server => {
149+
expect(server.address).toBeDefined();
155150
done();
156151
};
157-
158-
// When
159-
startNewTransaction(driver);
160152
});
161153

162154
it('should fail nicely when connecting with routing to standalone server', done => {

test/v1/examples.test.js

Lines changed: 6 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,9 @@ describe('examples', () => {
102102
// end::basic-auth[]
103103

104104
driver.onCompleted = () => {
105+
driver.close();
105106
done();
106107
};
107-
108-
const session = driver.session();
109-
session.run('RETURN 1').then(() => {
110-
session.close();
111-
driver.close();
112-
});
113108
});
114109

115110
it('config max retry time example', done => {
@@ -123,14 +118,9 @@ describe('examples', () => {
123118
// end::config-max-retry-time[]
124119

125120
driver.onCompleted = () => {
121+
driver.close();
126122
done();
127123
};
128-
129-
const session = driver.session();
130-
session.run('RETURN 1').then(() => {
131-
session.close();
132-
driver.close();
133-
});
134124
});
135125

136126
it('config trust example', done => {
@@ -144,19 +134,9 @@ describe('examples', () => {
144134
// end::config-trust[]
145135

146136
driver.onCompleted = () => {
137+
driver.close();
147138
done();
148139
};
149-
150-
driver.onError = error => {
151-
console.log(error);
152-
};
153-
154-
const session = driver.session();
155-
session.run('RETURN 1').then(() => {
156-
session.close();
157-
driver.close();
158-
}).catch(error => {
159-
});
160140
});
161141

162142
it('config unencrypted example', done => {
@@ -169,14 +149,9 @@ describe('examples', () => {
169149
// end::config-unencrypted[]
170150

171151
driver.onCompleted = () => {
152+
driver.close();
172153
done();
173154
};
174-
175-
const session = driver.session();
176-
session.run('RETURN 1').then(() => {
177-
session.close();
178-
driver.close();
179-
});
180155
});
181156

182157
it('custom auth example', done => {
@@ -191,14 +166,9 @@ describe('examples', () => {
191166
// end::custom-auth[]
192167

193168
driver.onCompleted = () => {
169+
driver.close();
194170
done();
195171
};
196-
197-
const session = driver.session();
198-
session.run('RETURN 1').then(() => {
199-
session.close();
200-
driver.close();
201-
});
202172
});
203173

204174
it('kerberos auth example', () => {
@@ -239,7 +209,7 @@ describe('examples', () => {
239209
// tag::driver-lifecycle[]
240210
const driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
241211

242-
driver.onCompleted = metadata => {
212+
driver.onCompleted = () => {
243213
console.log('Driver created');
244214
};
245215

0 commit comments

Comments
 (0)