Skip to content

Commit 54a0641

Browse files
authored
Merge pull request #353 from lutovich/1.6-http-session-close
Improvements to HTTP session
2 parents f8d3b8a + 91b4e6c commit 54a0641

10 files changed

+1105
-173
lines changed

src/v1/internal/http/http-driver.js

+4-14
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,20 @@
1919

2020
import Driver from '../../driver';
2121
import HttpSession from './http-session';
22+
import HttpSessionTracker from './http-session-tracker';
2223

2324
export default class HttpDriver extends Driver {
2425

2526
constructor(url, userAgent, token, config) {
2627
super(url, userAgent, token, config);
27-
this._sessionIdGenerator = 0;
28-
this._openSessions = {};
28+
this._sessionTracker = new HttpSessionTracker();
2929
}
3030

3131
session() {
32-
const id = this._sessionIdGenerator;
33-
this._sessionIdGenerator++;
34-
const session = new HttpSession(this._url, this._token, this._config);
35-
this._openSessions[id] = session;
36-
return session;
32+
return new HttpSession(this._url, this._token, this._config, this._sessionTracker);
3733
}
3834

3935
close() {
40-
Object.keys(this._openSessions).forEach(id => {
41-
const session = this._openSessions[id];
42-
if (session) {
43-
session.close();
44-
}
45-
delete this._openSessions[id];
46-
});
36+
return this._sessionTracker.close();
4737
}
4838
}
+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/**
2+
* Copyright (c) 2002-2018 "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 StreamObserver from '../stream-observer';
21+
import HttpResponseConverter from './http-response-converter';
22+
import {Neo4jError, SERVICE_UNAVAILABLE} from '../../error';
23+
24+
export default class HttpRequestRunner {
25+
26+
constructor(url, authToken) {
27+
this._url = url;
28+
this._authToken = authToken;
29+
this._converter = new HttpResponseConverter();
30+
}
31+
32+
/**
33+
* Send a HTTP request to begin a transaction.
34+
* @return {Promise<number>} promise resolved with the transaction id or rejected with an error.
35+
*/
36+
beginTransaction() {
37+
const url = beginTransactionUrl(this._url);
38+
return sendRequest('POST', url, null, this._authToken).then(responseJson => {
39+
const neo4jError = this._converter.extractError(responseJson);
40+
if (neo4jError) {
41+
throw neo4jError;
42+
}
43+
return this._converter.extractTransactionId(responseJson);
44+
});
45+
}
46+
47+
/**
48+
* Send a HTTP request to commit a transaction.
49+
* @param {number} transactionId id of the transaction to commit.
50+
* @return {Promise<void>} promise resolved if transaction got committed or rejected when commit failed.
51+
*/
52+
commitTransaction(transactionId) {
53+
const url = commitTransactionUrl(this._url, transactionId);
54+
return sendRequest('POST', url, null, this._authToken).then(responseJson => {
55+
const neo4jError = this._converter.extractError(responseJson);
56+
if (neo4jError) {
57+
throw neo4jError;
58+
}
59+
});
60+
}
61+
62+
/**
63+
* Send a HTTP request to rollback a transaction.
64+
* @param {number} transactionId id of the transaction to rollback.
65+
* @return {Promise<void>} promise resolved if transaction got rolled back or rejected when rollback failed.
66+
*/
67+
rollbackTransaction(transactionId) {
68+
const url = transactionUrl(this._url, transactionId);
69+
return sendRequest('DELETE', url, null, this._authToken).then(responseJson => {
70+
const neo4jError = this._converter.extractError(responseJson);
71+
if (neo4jError) {
72+
throw neo4jError;
73+
}
74+
});
75+
}
76+
77+
/**
78+
* Send a HTTP request to execute a query in a transaction with the given id.
79+
* @param {number} transactionId the transaction id.
80+
* @param {string} statement the cypher query.
81+
* @param {object} parameters the cypher query parameters.
82+
* @return {Promise<StreamObserver>} a promise resolved with {@link StreamObserver} containing either records or error.
83+
*/
84+
runQuery(transactionId, statement, parameters) {
85+
const streamObserver = new StreamObserver();
86+
const url = transactionUrl(this._url, transactionId);
87+
const body = createStatementJson(statement, parameters, this._converter, streamObserver);
88+
if (!body) {
89+
// unable to encode given statement and parameters, return a failed stream observer
90+
return Promise.resolve(streamObserver);
91+
}
92+
93+
return sendRequest('POST', url, body, this._authToken).then(responseJson => {
94+
processResponseJson(responseJson, this._converter, streamObserver);
95+
}).catch(error => {
96+
streamObserver.onError(error);
97+
}).then(() => {
98+
return streamObserver;
99+
});
100+
}
101+
}
102+
103+
function sendRequest(method, url, bodyString, authToken) {
104+
try {
105+
const options = {
106+
method: method,
107+
headers: createHttpHeaders(authToken),
108+
body: bodyString
109+
};
110+
111+
return new Promise((resolve, reject) => {
112+
fetch(url, options)
113+
.then(response => response.json())
114+
.then(responseJson => resolve(responseJson))
115+
.catch(error => reject(new Neo4jError(error.message, SERVICE_UNAVAILABLE)));
116+
});
117+
} catch (e) {
118+
return Promise.reject(e);
119+
}
120+
}
121+
122+
function createHttpHeaders(authToken) {
123+
const headers = new Headers();
124+
headers.append('Accept', 'application/json; charset=UTF-8');
125+
headers.append('Content-Type', 'application/json');
126+
headers.append('Authorization', 'Basic ' + btoa(authToken.principal + ':' + authToken.credentials));
127+
return headers;
128+
}
129+
130+
function createStatementJson(statement, parameters, converter, streamObserver) {
131+
try {
132+
return createStatementJsonOrThrow(statement, parameters, converter);
133+
} catch (e) {
134+
streamObserver.onError(e);
135+
return null;
136+
}
137+
}
138+
139+
function createStatementJsonOrThrow(statement, parameters, converter) {
140+
const encodedParameters = converter.encodeStatementParameters(parameters);
141+
return JSON.stringify({
142+
statements: [{
143+
statement: statement,
144+
parameters: encodedParameters,
145+
resultDataContents: ['row', 'graph'],
146+
includeStats: true
147+
}]
148+
});
149+
}
150+
151+
function processResponseJson(responseJson, converter, streamObserver) {
152+
if (!responseJson) {
153+
// request failed and there is no response
154+
return;
155+
}
156+
157+
try {
158+
processResponseJsonOrThrow(responseJson, converter, streamObserver);
159+
} catch (e) {
160+
streamObserver.onError(e);
161+
}
162+
}
163+
164+
function processResponseJsonOrThrow(responseJson, converter, streamObserver) {
165+
const neo4jError = converter.extractError(responseJson);
166+
if (neo4jError) {
167+
streamObserver.onError(neo4jError);
168+
} else {
169+
const recordMetadata = converter.extractRecordMetadata(responseJson);
170+
streamObserver.onCompleted(recordMetadata);
171+
172+
const rawRecords = converter.extractRawRecords(responseJson);
173+
rawRecords.forEach(rawRecord => streamObserver.onNext(rawRecord));
174+
175+
const statementMetadata = converter.extractStatementMetadata(responseJson);
176+
streamObserver.onCompleted(statementMetadata);
177+
}
178+
}
179+
180+
function beginTransactionUrl(baseUrl) {
181+
return createUrl(baseUrl, '/db/data/transaction');
182+
}
183+
184+
function commitTransactionUrl(baseUrl, transactionId) {
185+
return transactionUrl(baseUrl, transactionId) + '/commit';
186+
}
187+
188+
function transactionUrl(baseUrl, transactionId) {
189+
return beginTransactionUrl(baseUrl) + '/' + transactionId;
190+
}
191+
192+
function createUrl(baseUrl, path) {
193+
return `${baseUrl.scheme}://${baseUrl.host}:${baseUrl.port}${path}`;
194+
}

0 commit comments

Comments
 (0)