@neo4j-labs/experimental-query-api-wrapper 0.0.1-alpha05 → 0.0.1-alpha06

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Copyright (c) "Neo4j"
3
+ * Neo4j Sweden AB [https://neo4j.com]
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ export const NEO4J_QUERY_CONTENT_TYPE = 'application/vnd.neo4j.query';
18
+ export function encodeAuthToken(auth) {
19
+ switch (auth.scheme) {
20
+ case 'bearer':
21
+ return `Bearer ${btoa(auth.credentials)}`;
22
+ case 'basic':
23
+ return `Basic ${btoa(`${auth.principal}:${auth.credentials}`)}`;
24
+ default:
25
+ throw new Error(`Authorization scheme "${auth.scheme}" is not supported.`);
26
+ }
27
+ }
28
+ export function encodeTransactionBody(config) {
29
+ const body = {};
30
+ if (config?.bookmarks != null && !config.bookmarks.isEmpty()) {
31
+ body.bookmarks = config?.bookmarks?.values();
32
+ }
33
+ if (config?.txConfig.timeout != null) {
34
+ body.maxExecutionTime = config?.txConfig.timeout.toInt();
35
+ }
36
+ if (config?.impersonatedUser != null) {
37
+ body.impersonatedUser = config?.impersonatedUser;
38
+ }
39
+ if (config?.mode) {
40
+ body.accessMode = config.mode.toUpperCase();
41
+ }
42
+ return body;
43
+ }
@@ -17,6 +17,8 @@
17
17
  import { Connection, newError } from "neo4j-driver-core";
18
18
  import { ResultStreamObserver } from "./stream-observers";
19
19
  import { QueryRequestCodec, QueryResponseCodec } from "./query.codec";
20
+ import { BeginTransactionRequestCodec, BeginTransactionResponseCodec, CommitTransactionRequestCodec, CommitTransactionResponseCodec, RollbackTransactionRequestCodec, RollbackTransactionResponseCodec } from "./transaction.codec";
21
+ import Pipe from "./lang/pipe";
20
22
  let currentId = 0;
21
23
  export default class HttpConnection extends Connection {
22
24
  constructor(config) {
@@ -29,43 +31,36 @@ export default class HttpConnection extends Connection {
29
31
  this._log = config.logger;
30
32
  this._errorHandler = config.errorHandler;
31
33
  this._open = true;
34
+ this._currentTx = undefined;
35
+ this._workPipe = new Pipe(config.logger);
36
+ this._abortControllers = [];
37
+ this._sessionAffinityHeader = config.config.httpSessionAffinityHeader ?? 'neo4j-cluster-affinity';
32
38
  }
33
39
  run(query, parameters, config) {
34
40
  const observer = new ResultStreamObserver({
35
41
  highRecordWatermark: config?.highRecordWatermark ?? Number.MAX_SAFE_INTEGER,
36
42
  lowRecordWatermark: config?.lowRecordWatermark ?? Number.MIN_SAFE_INTEGER,
37
43
  afterComplete: config?.afterComplete,
44
+ beforeError: config?.beforeError,
38
45
  server: this._address,
39
46
  });
40
47
  const requestCodec = QueryRequestCodec.of(this._auth, query, parameters, config);
41
- this._abortController = new AbortController();
42
- const request = {
43
- url: this._getTransactionApi(config?.database),
44
- method: 'POST',
45
- mode: 'cors',
46
- headers: {
47
- 'Content-Type': requestCodec.contentType,
48
- Accept: requestCodec.accept,
49
- Authorization: requestCodec.authorization,
50
- },
51
- signal: this._abortController.signal,
52
- body: JSON.stringify(requestCodec.body)
53
- };
54
- this._log?.debug(`${this} REQUEST: ${JSON.stringify(request)} `);
55
- fetch(request.url, request).
56
- then(async (res) => {
57
- return [res.headers.get('content-type'), (await res.json())];
58
- })
59
- .catch((error) => this._handleAndReThrown(newError(`Failure accessing "${request.url}"`, 'SERVICE_UNAVAILABLE', error)))
60
- .catch((error) => observer.onError(error))
61
- .then(async ([contentType, rawQueryResponse]) => {
62
- if (rawQueryResponse == null) {
63
- // is already dead
64
- return;
65
- }
48
+ const abortController = this._newAbortController();
49
+ this._workPipe.attach(async () => {
50
+ const request = {
51
+ url: this._getTransactionApi(config?.database),
52
+ method: 'POST',
53
+ mode: 'cors',
54
+ headers: this._headers(requestCodec),
55
+ signal: abortController.signal,
56
+ body: JSON.stringify(requestCodec.body)
57
+ };
58
+ this._log?.debug(`${this} REQUEST: ${JSON.stringify(request)}`);
59
+ const res = await fetch(request.url, request);
60
+ const { body: rawQueryResponse, headers: [contentType] } = await readBodyAndReaders(request.url, res, 'content-type');
66
61
  this._log?.debug(`${this} ${JSON.stringify(rawQueryResponse)}`);
67
62
  const batchSize = config?.fetchSize ?? Number.MAX_SAFE_INTEGER;
68
- const codec = QueryResponseCodec.of(this._config, contentType, rawQueryResponse);
63
+ const codec = QueryResponseCodec.of(this._config, contentType ?? '', rawQueryResponse);
69
64
  if (codec.error) {
70
65
  throw codec.error;
71
66
  }
@@ -88,18 +83,145 @@ export default class HttpConnection extends Connection {
88
83
  }
89
84
  observer.onCompleted(codec.meta);
90
85
  })
91
- .catch(this._handleAndReThrown.bind(this))
92
- .catch(error => observer.onError(error))
93
- .finally(() => {
94
- this._abortController = undefined;
86
+ .catch(this._catch(observer))
87
+ .finally(this._finally(abortController));
88
+ return observer;
89
+ }
90
+ _headers(requestCodec) {
91
+ const headers = {
92
+ 'Content-Type': requestCodec.contentType,
93
+ Accept: requestCodec.accept,
94
+ Authorization: requestCodec.authorization,
95
+ };
96
+ if (this._currentTx?.affinity != null) {
97
+ headers[this._sessionAffinityHeader] = this._currentTx.affinity;
98
+ }
99
+ return headers;
100
+ }
101
+ beginTransaction(config) {
102
+ const observer = new ResultStreamObserver({
103
+ server: this._address,
104
+ afterComplete: config?.afterComplete,
105
+ beforeError: config.beforeError,
106
+ highRecordWatermark: Number.MAX_SAFE_INTEGER,
107
+ lowRecordWatermark: Number.MIN_SAFE_INTEGER,
95
108
  });
109
+ observer.prepareToHandleSingleResponse();
110
+ const requestCodec = BeginTransactionRequestCodec.of(this._auth, config);
111
+ const abortController = this._newAbortController();
112
+ this._workPipe.attach(async () => {
113
+ const request = {
114
+ url: this._getExplicityTransactionApi(config?.database),
115
+ method: 'POST',
116
+ mode: 'cors',
117
+ headers: this._headers(requestCodec),
118
+ signal: abortController.signal,
119
+ body: JSON.stringify(requestCodec.body)
120
+ };
121
+ this._log?.debug(`${this} REQUEST: ${JSON.stringify(request)} `);
122
+ const res = await fetch(request.url, request);
123
+ const { body: rawBeginTransactionResponse, headers: [contentType, affinity] } = await readBodyAndReaders(request.url, res, 'content-type', this._sessionAffinityHeader);
124
+ this._log?.debug(`${this} ${JSON.stringify(rawBeginTransactionResponse)}`);
125
+ const codec = BeginTransactionResponseCodec.of(this._config, contentType ?? '', rawBeginTransactionResponse);
126
+ if (codec.error != null) {
127
+ throw codec.error;
128
+ }
129
+ this._currentTx = {
130
+ id: codec.id,
131
+ host: codec.host,
132
+ expires: codec.expires,
133
+ database: config?.database,
134
+ };
135
+ if (affinity != null) {
136
+ this._currentTx.affinity = affinity;
137
+ }
138
+ observer.onCompleted({});
139
+ })
140
+ .catch(this._catch(observer))
141
+ .finally(this._finally(abortController));
142
+ return observer;
143
+ }
144
+ commitTransaction(config) {
145
+ const observer = new ResultStreamObserver({
146
+ server: this._address,
147
+ afterComplete: config?.afterComplete,
148
+ beforeError: config.beforeError,
149
+ highRecordWatermark: Number.MAX_SAFE_INTEGER,
150
+ lowRecordWatermark: Number.MIN_SAFE_INTEGER,
151
+ });
152
+ observer.prepareToHandleSingleResponse();
153
+ const requestCodec = CommitTransactionRequestCodec.of(this._auth, config);
154
+ const abortController = this._newAbortController();
155
+ this._workPipe.attach(async () => {
156
+ const request = {
157
+ url: this._getTransactionCommitApi(),
158
+ method: 'POST',
159
+ mode: 'cors',
160
+ headers: this._headers(requestCodec),
161
+ signal: abortController.signal,
162
+ body: JSON.stringify(requestCodec.body)
163
+ };
164
+ this._log?.debug(`${this} REQUEST: ${JSON.stringify(request)} `);
165
+ const res = await fetch(request.url, request);
166
+ const { body: rawCommitTransactionResponse, headers: [contentType] } = await readBodyAndReaders(request.url, res, 'contentType');
167
+ this._log?.debug(`${this} ${JSON.stringify(rawCommitTransactionResponse)}`);
168
+ const codec = CommitTransactionResponseCodec.of(this._config, contentType ?? '', rawCommitTransactionResponse);
169
+ if (codec.error) {
170
+ throw codec.error;
171
+ }
172
+ this._currentTx = undefined;
173
+ observer.onCompleted(codec.meta);
174
+ })
175
+ .catch(this._catch(observer))
176
+ .finally(this._finally(abortController));
96
177
  return observer;
97
178
  }
98
- _handleAndReThrown(error) {
99
- throw this._errorHandler(error);
179
+ rollbackTransaction(config) {
180
+ const observer = new ResultStreamObserver({
181
+ server: this._address,
182
+ afterComplete: config?.afterComplete,
183
+ beforeError: config.beforeError,
184
+ highRecordWatermark: Number.MAX_SAFE_INTEGER,
185
+ lowRecordWatermark: Number.MIN_SAFE_INTEGER,
186
+ });
187
+ observer.prepareToHandleSingleResponse();
188
+ const requestCodec = RollbackTransactionRequestCodec.of(this._auth, config);
189
+ const abortController = this._newAbortController();
190
+ this._workPipe.attach(async () => {
191
+ const request = {
192
+ url: this._getTransactionApi(this._currentTx?.database),
193
+ method: 'DELETE',
194
+ mode: 'cors',
195
+ headers: this._headers(requestCodec),
196
+ signal: abortController.signal,
197
+ body: JSON.stringify(requestCodec.body)
198
+ };
199
+ this._log?.debug(`${this} REQUEST: ${JSON.stringify(request)} `);
200
+ const res = await fetch(request.url, request);
201
+ const { body: rawRollbackTransactionResponse, headers: [contentType] } = await readBodyAndReaders(request.url, res, 'content-type');
202
+ this._log?.debug(`${this} ${JSON.stringify(rawRollbackTransactionResponse)}`);
203
+ const codec = RollbackTransactionResponseCodec.of(this._config, contentType ?? '', rawRollbackTransactionResponse);
204
+ if (codec.error) {
205
+ throw codec.error;
206
+ }
207
+ this._currentTx = undefined;
208
+ observer.onCompleted(codec.meta);
209
+ })
210
+ .catch(this._catch(observer))
211
+ .finally(this._finally(abortController));
212
+ return observer;
100
213
  }
101
214
  _getTransactionApi(database) {
102
- return this._queryEndpoint.replace('{databaseName}', database);
215
+ if (this._currentTx === undefined) {
216
+ return this._queryEndpoint.replace('{databaseName}', database);
217
+ }
218
+ return this._queryEndpoint.replace('{databaseName}', this._currentTx.database) + `/tx/${this._currentTx.id}`;
219
+ }
220
+ _getTransactionCommitApi() {
221
+ return this._queryEndpoint.replace('{databaseName}', this._currentTx?.database) + `/tx/${this._currentTx?.id}/commit`;
222
+ }
223
+ _getExplicityTransactionApi(database) {
224
+ return this._queryEndpoint.replace('{databaseName}', database) + '/tx';
103
225
  }
104
226
  static async discover({ scheme, address }) {
105
227
  return await fetch(`${scheme}://${address.asHostPort()}`, {
@@ -112,7 +234,8 @@ export default class HttpConnection extends Connection {
112
234
  if (typeof json.query !== 'string') {
113
235
  throw new Error('Query API is not available');
114
236
  }
115
- return { query: json.query, version: json.neo4j_version, edition: json.neo4j_edition };
237
+ const query = json.query.endsWith('/') ? json.query.substring(0, json.query.length - 1) : json.query;
238
+ return { query, version: json.neo4j_version, edition: json.neo4j_edition };
116
239
  })
117
240
  .catch(e => {
118
241
  throw newError(`Failure discovering endpoints. Caused by: ${e.message}`, 'SERVICE_UNAVAILABLE', e);
@@ -140,19 +263,52 @@ export default class HttpConnection extends Connection {
140
263
  return this._open;
141
264
  }
142
265
  hasOngoingObservableRequests() {
143
- return this._abortController != null;
266
+ return this._abortControllers.length > 0;
144
267
  }
145
268
  async resetAndFlush() {
146
- this._abortController?.abort(newError('User aborted operation.'));
269
+ this._abortControllers.forEach((controller) => controller.abort(newError('User aborted operation.')));
270
+ this._abortControllers = [];
271
+ this._currentTx = undefined;
272
+ this._workPipe.recover();
147
273
  }
148
274
  release() {
275
+ this._workPipe.recover();
149
276
  return this._release();
150
277
  }
151
278
  async close() {
152
- this._abortController?.abort(newError('Aborted since connection is being closed.'));
279
+ this._abortControllers.forEach((controller) => controller.abort(newError('Aborted since connection is being closed.')));
280
+ this._abortControllers = [];
153
281
  this._open = false;
154
282
  }
155
283
  toString() {
156
284
  return `HttpConnection [${this._id}]`;
157
285
  }
286
+ _catch(observer) {
287
+ return error => {
288
+ this._currentTx = undefined;
289
+ const newError = this._errorHandler(error);
290
+ observer.onError(newError);
291
+ };
292
+ }
293
+ _finally(abortController) {
294
+ this._abortControllers = this._abortControllers.filter(ac => ac !== abortController);
295
+ return undefined;
296
+ }
297
+ _newAbortController() {
298
+ const abortController = new AbortController();
299
+ this._abortControllers.push(abortController);
300
+ return abortController;
301
+ }
302
+ }
303
+ async function readBodyAndReaders(url, response, ...headers) {
304
+ try {
305
+ const text = await response.text();
306
+ return {
307
+ body: text !== '' ? JSON.parse(text) : {},
308
+ headers: headers.map(header => response.headers.get(header))
309
+ };
310
+ }
311
+ catch (error) {
312
+ throw newError(`Failure accessing "${url}"`, 'SERVICE_UNAVAILABLE', error);
313
+ }
158
314
  }
@@ -0,0 +1,17 @@
1
+ export default class Pipe {
2
+ constructor(logger) {
3
+ this.logger = logger;
4
+ this.promise = Promise.resolve();
5
+ }
6
+ attach(work) {
7
+ this.promise = this.promise.then(work);
8
+ return this.promise;
9
+ }
10
+ recover() {
11
+ this.promise = this.promise.catch(error => {
12
+ if (this.logger?.isDebugEnabled() === true) {
13
+ this.logger?.debug(`Recovering from ${error}`);
14
+ }
15
+ });
16
+ }
17
+ }
@@ -15,7 +15,7 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { newError, Node, Relationship, int, error, Time, Date, LocalTime, Point, DateTime, LocalDateTime, Duration, isInt, isPoint, isDuration, isLocalTime, isTime, isDate, isLocalDateTime, isDateTime, isRelationship, isPath, isNode, isPathSegment, Path, PathSegment, internal, isUnboundRelationship } from "neo4j-driver-core";
18
- const NEO4J_QUERY_CONTENT_TYPE = 'application/vnd.neo4j.query';
18
+ import { NEO4J_QUERY_CONTENT_TYPE, encodeAuthToken, encodeTransactionBody } from "./codec";
19
19
  export class QueryResponseCodec {
20
20
  static of(config, contentType, response) {
21
21
  if (isSuccess(response)) {
@@ -415,14 +415,7 @@ export class QueryRequestCodec {
415
415
  return `${NEO4J_QUERY_CONTENT_TYPE}, application/json`;
416
416
  }
417
417
  get authorization() {
418
- switch (this._auth.scheme) {
419
- case 'bearer':
420
- return `Bearer ${btoa(this._auth.credentials)}`;
421
- case 'basic':
422
- return `Basic ${btoa(`${this._auth.principal}:${this._auth.credentials}`)}`;
423
- default:
424
- throw new Error(`Authorization scheme "${this._auth.scheme}" is not supported.`);
425
- }
418
+ return encodeAuthToken(this._auth);
426
419
  }
427
420
  get body() {
428
421
  if (this._body != null) {
@@ -430,23 +423,12 @@ export class QueryRequestCodec {
430
423
  }
431
424
  this._body = {
432
425
  statement: this._query,
433
- includeCounters: true
426
+ includeCounters: true,
427
+ ...encodeTransactionBody(this._config)
434
428
  };
435
429
  if (this._parameters != null && Object.getOwnPropertyNames(this._parameters).length !== 0) {
436
430
  this._body.parameters = this._encodeParameters(this._parameters);
437
431
  }
438
- if (this._config?.bookmarks != null && !this._config.bookmarks.isEmpty()) {
439
- this._body.bookmarks = this._config?.bookmarks?.values();
440
- }
441
- if (this._config?.txConfig.timeout != null) {
442
- this._body.maxExecutionTime = this._config?.txConfig.timeout.toInt();
443
- }
444
- if (this._config?.impersonatedUser != null) {
445
- this._body.impersonatedUser = this._config?.impersonatedUser;
446
- }
447
- if (this._config?.mode) {
448
- this._body.accessMode = this._config.mode.toUpperCase();
449
- }
450
432
  return this._body;
451
433
  }
452
434
  _encodeParameters(parameters) {
@@ -0,0 +1,255 @@
1
+ /**
2
+ * Copyright (c) "Neo4j"
3
+ * Neo4j Sweden AB [https://neo4j.com]
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { error, newError } from "neo4j-driver-core";
18
+ import { NEO4J_QUERY_CONTENT_TYPE, encodeAuthToken, encodeTransactionBody } from "./codec";
19
+ export class BeginTransactionRequestCodec {
20
+ static of(auth, config) {
21
+ return new BeginTransactionRequestCodec(auth, config);
22
+ }
23
+ constructor(_auth, _config) {
24
+ this._auth = _auth;
25
+ this._config = _config;
26
+ }
27
+ get contentType() {
28
+ return 'application/json';
29
+ }
30
+ get accept() {
31
+ return `${NEO4J_QUERY_CONTENT_TYPE}, application/json`;
32
+ }
33
+ get authorization() {
34
+ return encodeAuthToken(this._auth);
35
+ }
36
+ get body() {
37
+ if (this._body != null) {
38
+ return this._body;
39
+ }
40
+ this._body = encodeTransactionBody(this._config);
41
+ return this._body;
42
+ }
43
+ }
44
+ export class BeginTransactionResponseCodec {
45
+ static of(config, contentType, response) {
46
+ if (isSuccess(response)) {
47
+ return new BeginTransactionSuccessResponseCodec(config, response);
48
+ }
49
+ return new BeginTransactionFailureResponseCodec(response.errors?.length > 0 ?
50
+ newError(response.errors[0].message,
51
+ // TODO: REMOVE THE ?? AND .ERROR WHEN SERVER IS FIXED
52
+ response.errors[0].code ?? response.errors[0].error) :
53
+ newError('Server replied an empty error response', error.PROTOCOL_ERROR));
54
+ }
55
+ get error() {
56
+ throw new Error('Not implemented');
57
+ }
58
+ get id() {
59
+ throw new Error('Not implemented');
60
+ }
61
+ get expires() {
62
+ throw new Error('Not implemented');
63
+ }
64
+ get host() {
65
+ throw new Error('Not implemented');
66
+ }
67
+ }
68
+ class BeginTransactionSuccessResponseCodec extends BeginTransactionResponseCodec {
69
+ constructor(_config, _response) {
70
+ super();
71
+ this._config = _config;
72
+ this._response = _response;
73
+ }
74
+ get error() {
75
+ return undefined;
76
+ }
77
+ get id() {
78
+ return this._response.transaction.id;
79
+ }
80
+ get expires() {
81
+ return new Date(this._response.transaction.expires);
82
+ }
83
+ get host() {
84
+ return this._response.transaction.tx_host;
85
+ }
86
+ }
87
+ class BeginTransactionFailureResponseCodec extends BeginTransactionResponseCodec {
88
+ constructor(_error) {
89
+ super();
90
+ this._error = _error;
91
+ }
92
+ get error() {
93
+ return this._error;
94
+ }
95
+ get id() {
96
+ throw this._error;
97
+ }
98
+ get expires() {
99
+ throw this._error;
100
+ }
101
+ get host() {
102
+ throw this._error;
103
+ }
104
+ }
105
+ export class CommitTransactionRequestCodec {
106
+ static of(auth, config) {
107
+ return new CommitTransactionRequestCodec(auth, config);
108
+ }
109
+ constructor(_auth, _config) {
110
+ this._auth = _auth;
111
+ this._config = _config;
112
+ }
113
+ get contentType() {
114
+ return 'application/json';
115
+ }
116
+ get accept() {
117
+ return `${NEO4J_QUERY_CONTENT_TYPE}, application/json`;
118
+ }
119
+ get body() {
120
+ return {};
121
+ }
122
+ get authorization() {
123
+ return encodeAuthToken(this._auth);
124
+ }
125
+ }
126
+ export class CommitTransactionResponseCodec {
127
+ static of(config, contentType, response) {
128
+ if (isCommitSuccess(response)) {
129
+ return new CommitTransactionSuccessResponseCodec(config, response);
130
+ }
131
+ return new CommitTransactionFailureResponseCodec(response.errors?.length > 0 ?
132
+ newError(response.errors[0].message,
133
+ // TODO: REMOVE THE ?? AND .ERROR WHEN SERVER IS FIXED
134
+ response.errors[0].code ?? response.errors[0].error) :
135
+ newError('Server replied an empty error response', error.PROTOCOL_ERROR));
136
+ }
137
+ get error() {
138
+ throw new Error('Not implemented');
139
+ }
140
+ get meta() {
141
+ throw new Error('Not implemented');
142
+ }
143
+ }
144
+ class CommitTransactionSuccessResponseCodec extends CommitTransactionResponseCodec {
145
+ constructor(_config, _response) {
146
+ super();
147
+ this._config = _config;
148
+ this._response = _response;
149
+ }
150
+ get error() {
151
+ return undefined;
152
+ }
153
+ get meta() {
154
+ return {
155
+ bookmark: this._response.bookmarks
156
+ };
157
+ }
158
+ }
159
+ class CommitTransactionFailureResponseCodec extends CommitTransactionResponseCodec {
160
+ constructor(_error) {
161
+ super();
162
+ this._error = _error;
163
+ }
164
+ get error() {
165
+ return this._error;
166
+ }
167
+ get meta() {
168
+ throw this._error;
169
+ }
170
+ }
171
+ export class RollbackTransactionRequestCodec {
172
+ static of(auth, config) {
173
+ return new RollbackTransactionRequestCodec(auth, config);
174
+ }
175
+ constructor(_auth, _config) {
176
+ this._auth = _auth;
177
+ this._config = _config;
178
+ }
179
+ get body() {
180
+ return {};
181
+ }
182
+ get contentType() {
183
+ return 'application/json';
184
+ }
185
+ get accept() {
186
+ return `${NEO4J_QUERY_CONTENT_TYPE}, application/json`;
187
+ }
188
+ get authorization() {
189
+ return encodeAuthToken(this._auth);
190
+ }
191
+ }
192
+ export class RollbackTransactionResponseCodec {
193
+ static of(config, contentType, response) {
194
+ if (isRollbackSuccess(response)) {
195
+ return new RollbackTransactionSuccessResponseCodec(config, response);
196
+ }
197
+ return new RollbackTransactionFailureResponseCodec(response.errors?.length > 0 ?
198
+ newError(response.errors[0].message,
199
+ // TODO: REMOVE THE ?? AND .ERROR WHEN SERVER IS FIXED
200
+ response.errors[0].code ?? response.errors[0].error) :
201
+ newError('Server replied an empty error response', error.PROTOCOL_ERROR));
202
+ }
203
+ get error() {
204
+ throw new Error('Not implemented');
205
+ }
206
+ get meta() {
207
+ throw new Error('Not implemented');
208
+ }
209
+ }
210
+ class RollbackTransactionSuccessResponseCodec extends RollbackTransactionResponseCodec {
211
+ constructor(_config, _response) {
212
+ super();
213
+ this._config = _config;
214
+ this._response = _response;
215
+ }
216
+ get error() {
217
+ return undefined;
218
+ }
219
+ get meta() {
220
+ return {};
221
+ }
222
+ }
223
+ class RollbackTransactionFailureResponseCodec extends RollbackTransactionResponseCodec {
224
+ constructor(_error) {
225
+ super();
226
+ this._error = _error;
227
+ }
228
+ get error() {
229
+ return this._error;
230
+ }
231
+ get meta() {
232
+ throw this._error;
233
+ }
234
+ }
235
+ function isSuccess(obj) {
236
+ // @ts-expect-error
237
+ if (obj.errors != null) {
238
+ return false;
239
+ }
240
+ return true;
241
+ }
242
+ function isCommitSuccess(obj) {
243
+ // @ts-expect-error
244
+ if (obj.errors != null) {
245
+ return false;
246
+ }
247
+ return true;
248
+ }
249
+ function isRollbackSuccess(obj) {
250
+ // @ts-expect-error
251
+ if (obj.errors != null) {
252
+ return false;
253
+ }
254
+ return true;
255
+ }
package/lib/index.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { auth, bookmarkManager, authTokenManagers, staticAuthTokenManager, Connection, ConnectionProvider, Date, DateTime, Driver, Duration, error, inSafeRange, int, Integer, internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, isPoint, isRelationship, isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, Neo4jError, Node, Notification, notificationCategory, notificationSeverityLevel, Path, PathSegment, Plan, Point, ProfiledPlan, QueryStatistics, Record, Relationship, Result, ResultSummary, resultTransformers, ServerInfo, Session, Time, toNumber, toString, UnboundRelationship, driver as coreDriver, } from 'neo4j-driver-core';
17
+ import { auth, bookmarkManager, authTokenManagers, staticAuthTokenManager, Connection, ConnectionProvider, Date, DateTime, Driver, Duration, error, inSafeRange, int, Integer, internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, isPoint, isRelationship, isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, Neo4jError, Node, Notification, notificationCategory, notificationSeverityLevel, Path, PathSegment, Plan, Point, ProfiledPlan, QueryStatistics, Record, Relationship, Result, ResultSummary, resultTransformers, ServerInfo, Session, Time, toNumber, toString, UnboundRelationship, driver as coreDriver, Transaction, TransactionPromise, ManagedTransaction } from 'neo4j-driver-core';
18
18
  import { logging } from './logging';
19
19
  import { WrapperImpl } from './wrapper.impl';
20
20
  import { HttpConnectionProvider } from './http-connection';
@@ -186,6 +186,9 @@ const forExport = {
186
186
  Result,
187
187
  Record,
188
188
  ResultSummary,
189
+ Transaction,
190
+ TransactionPromise,
191
+ ManagedTransaction,
189
192
  Node,
190
193
  Relationship,
191
194
  UnboundRelationship,
@@ -213,5 +216,5 @@ const forExport = {
213
216
  notificationSeverityLevel,
214
217
  wrapper
215
218
  };
216
- export { authTokenManagers, int, isInt, isPoint, isDuration, isLocalTime, isTime, isDate, isLocalDateTime, isDateTime, isNode, isPath, isPathSegment, isRelationship, isUnboundRelationship, integer, Neo4jError, isRetriableError, auth, logging, types, session, error, graph, spatial, temporal, Driver, Result, Record, ResultSummary, Node, Relationship, UnboundRelationship, PathSegment, Path, Integer, Plan, ProfiledPlan, QueryStatistics, Notification, ServerInfo, Session, Point, Duration, LocalTime, Time, Date, LocalDateTime, DateTime, ConnectionProvider, Connection, bookmarkManager, resultTransformers, notificationCategory, notificationSeverityLevel, wrapper };
219
+ export { authTokenManagers, int, isInt, isPoint, isDuration, isLocalTime, isTime, isDate, isLocalDateTime, isDateTime, isNode, isPath, isPathSegment, isRelationship, isUnboundRelationship, integer, Neo4jError, isRetriableError, auth, logging, types, session, error, graph, spatial, temporal, Driver, Result, Record, ResultSummary, Node, Relationship, UnboundRelationship, PathSegment, Path, Integer, Plan, ProfiledPlan, QueryStatistics, Notification, ServerInfo, Session, Transaction, TransactionPromise, ManagedTransaction, Point, Duration, LocalTime, Time, Date, LocalDateTime, DateTime, ConnectionProvider, Connection, bookmarkManager, resultTransformers, notificationCategory, notificationSeverityLevel, wrapper };
217
220
  export default forExport;
@@ -8,6 +8,15 @@ export default class WrapperSessionImpl {
8
8
  run(query, parameters, transactionConfig) {
9
9
  return this.session.run(query, parameters, transactionConfig);
10
10
  }
11
+ beginTransaction(transactionConfig) {
12
+ return this.session.beginTransaction(transactionConfig);
13
+ }
14
+ executeRead(transactionWork, transactionConfig) {
15
+ return this.session.executeRead(transactionWork, transactionConfig);
16
+ }
17
+ executeWrite(transactionWork, transactionConfig) {
18
+ return this.session.executeWrite(transactionWork, transactionConfig);
19
+ }
11
20
  lastBookmarks() {
12
21
  return this.session.lastBookmarks();
13
22
  }
@@ -6,6 +6,13 @@ export class WrapperImpl {
6
6
  close() {
7
7
  return this.driver.close();
8
8
  }
9
+ executeQuery(query, parameters, config) {
10
+ validateDatabase(config);
11
+ return this.driver.executeQuery(query, parameters, config);
12
+ }
13
+ get executeQueryBookmarkManager() {
14
+ return this.driver.executeQueryBookmarkManager;
15
+ }
9
16
  verifyConnectivity(config) {
10
17
  validateDatabase(config);
11
18
  return this.driver.verifyConnectivity(config);
@@ -33,7 +40,7 @@ export class WrapperImpl {
33
40
  }
34
41
  }
35
42
  function validateDatabase(config) {
36
- if (config.database == null || config.database === '') {
37
- throw new TypeError(`database must be a non-empty string, but got ${config.database}`);
43
+ if (config == null || config.database == null || config.database === '') {
44
+ throw new TypeError(`database must be a non-empty string, but got ${config?.database}`);
38
45
  }
39
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neo4j-labs/experimental-query-api-wrapper",
3
- "version": "0.0.1-alpha05",
3
+ "version": "0.0.1-alpha06",
4
4
  "description": "Experimental wrapper library to access Neo4j Database using Query API with a neo4j-driver-like interface.",
5
5
  "main": "lib/index.js",
6
6
  "types": "types/index.d.ts",
@@ -42,7 +42,7 @@
42
42
  "typescript": "^4.9.5"
43
43
  },
44
44
  "dependencies": {
45
- "neo4j-driver-core": "^5.24.1"
45
+ "neo4j-driver-core": "^5.26.0"
46
46
  },
47
47
  "engines": {
48
48
  "node": ">=18.0.0"
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Copyright (c) "Neo4j"
3
+ * Neo4j Sweden AB [https://neo4j.com]
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { types } from "neo4j-driver-core";
18
+ import { BeginTransactionConfig } from "neo4j-driver-core/types/connection";
19
+ export declare const NEO4J_QUERY_CONTENT_TYPE = "application/vnd.neo4j.query";
20
+ export declare function encodeAuthToken(auth: types.AuthToken): string;
21
+ export declare function encodeTransactionBody(config?: Pick<BeginTransactionConfig, 'bookmarks' | 'txConfig' | 'mode' | 'impersonatedUser'>): Record<string, unknown>;
@@ -15,14 +15,15 @@
15
15
  * limitations under the License.
16
16
  */
17
17
  import { Connection, types, internal } from "neo4j-driver-core";
18
- import { RunQueryConfig } from "neo4j-driver-core/types/connection";
18
+ import { BeginTransactionConfig, CommitTransactionConfig, RunQueryConfig } from "neo4j-driver-core/types/connection";
19
+ import { WrapperConfig } from "../types";
19
20
  export type HttpScheme = 'http' | 'https';
20
21
  export interface HttpConnectionConfig {
21
22
  release: () => Promise<void>;
22
23
  auth: types.AuthToken;
23
24
  queryEndpoint: string;
24
25
  address: internal.serverAddress.ServerAddress;
25
- config: types.InternalConfig;
26
+ config: WrapperConfig;
26
27
  logger: internal.logger.Logger;
27
28
  errorHandler: (error: Error & {
28
29
  code: string;
@@ -35,15 +36,23 @@ export default class HttpConnection extends Connection {
35
36
  private _queryEndpoint;
36
37
  private _address;
37
38
  private _config;
38
- private _abortController?;
39
+ private _abortControllers;
39
40
  private _log?;
41
+ private _sessionAffinityHeader;
40
42
  private _id;
41
43
  private _errorHandler;
42
44
  private _open;
45
+ private _currentTx;
46
+ private _workPipe;
43
47
  constructor(config: HttpConnectionConfig);
44
48
  run(query: string, parameters?: Record<string, unknown> | undefined, config?: RunQueryConfig | undefined): internal.observer.ResultStreamObserver;
45
- private _handleAndReThrown;
49
+ private _headers;
50
+ beginTransaction(config: BeginTransactionConfig): internal.observer.ResultStreamObserver;
51
+ commitTransaction(config: CommitTransactionConfig): internal.observer.ResultStreamObserver;
52
+ rollbackTransaction(config: CommitTransactionConfig): internal.observer.ResultStreamObserver;
46
53
  private _getTransactionApi;
54
+ private _getTransactionCommitApi;
55
+ private _getExplicityTransactionApi;
47
56
  static discover({ scheme, address }: {
48
57
  scheme: HttpScheme;
49
58
  address: internal.serverAddress.ServerAddress;
@@ -64,4 +73,7 @@ export default class HttpConnection extends Connection {
64
73
  release(): Promise<void>;
65
74
  close(): Promise<void>;
66
75
  toString(): string;
76
+ private _catch;
77
+ private _finally;
78
+ private _newAbortController;
67
79
  }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Copyright (c) "Neo4j"
3
+ * Neo4j Sweden AB [https://neo4j.com]
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { internal } from "neo4j-driver-core";
18
+ export default class Pipe {
19
+ private logger?;
20
+ private promise;
21
+ constructor(logger?: internal.logger.Logger | undefined);
22
+ attach(work: () => Promise<void> | void): Promise<void>;
23
+ recover(): void;
24
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Copyright (c) "Neo4j"
3
+ * Neo4j Sweden AB [https://neo4j.com]
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { types } from "neo4j-driver-core";
18
+ import { BeginTransactionConfig } from "neo4j-driver-core/types/connection";
19
+ export type BeginTransactionRequestCodecConfig = Pick<BeginTransactionConfig, 'bookmarks' | 'txConfig' | 'mode' | 'impersonatedUser'>;
20
+ export type CommitTransactionRequestCodecConfig = {};
21
+ export type RollbackTransactionRequestCodecConfig = {};
22
+ export type RawTransaction = {
23
+ id: string;
24
+ expires: string;
25
+ tx_host?: string;
26
+ };
27
+ export type RawBeginTransactionSuccessResponse = {
28
+ transaction: RawTransaction;
29
+ };
30
+ export type RawTransactionError = {
31
+ code: string;
32
+ message: string;
33
+ error?: string;
34
+ };
35
+ export type RawTransactionFailuresResponse = {
36
+ errors: RawTransactionError[];
37
+ };
38
+ export type RawBeginTransactionResponse = RawBeginTransactionSuccessResponse | RawTransactionFailuresResponse;
39
+ export declare class BeginTransactionRequestCodec {
40
+ private _auth;
41
+ private _config?;
42
+ private _body?;
43
+ static of(auth: types.AuthToken, config?: BeginTransactionRequestCodecConfig | undefined): BeginTransactionRequestCodec;
44
+ private constructor();
45
+ get contentType(): string;
46
+ get accept(): string;
47
+ get authorization(): string;
48
+ get body(): Record<string, unknown>;
49
+ }
50
+ export declare class BeginTransactionResponseCodec {
51
+ static of(config: types.InternalConfig, contentType: string, response: RawBeginTransactionResponse): BeginTransactionResponseCodec;
52
+ get error(): Error | undefined;
53
+ get id(): string;
54
+ get expires(): Date;
55
+ get host(): string | undefined;
56
+ }
57
+ export type RawCommitTransactionSuccessResponse = {
58
+ bookmarks: string[] | undefined;
59
+ };
60
+ export type RawCommitTransactionResponse = RawCommitTransactionSuccessResponse | RawTransactionFailuresResponse;
61
+ export declare class CommitTransactionRequestCodec {
62
+ private _auth;
63
+ private _config?;
64
+ static of(auth: types.AuthToken, config?: CommitTransactionRequestCodecConfig | undefined): CommitTransactionRequestCodec;
65
+ private constructor();
66
+ get contentType(): string;
67
+ get accept(): string;
68
+ get body(): Record<string, unknown>;
69
+ get authorization(): string;
70
+ }
71
+ export declare class CommitTransactionResponseCodec {
72
+ static of(config: types.InternalConfig, contentType: string, response: RawCommitTransactionResponse): CommitTransactionResponseCodec;
73
+ get error(): Error | undefined;
74
+ get meta(): Record<string, unknown>;
75
+ }
76
+ export type RawRollbackTransactionSuccessResponse = {
77
+ e?: any;
78
+ };
79
+ export type RawRollbackTransactionResponse = RawRollbackTransactionSuccessResponse | RawTransactionFailuresResponse;
80
+ export declare class RollbackTransactionRequestCodec {
81
+ private _auth;
82
+ private _config?;
83
+ static of(auth: types.AuthToken, config?: RollbackTransactionRequestCodecConfig | undefined): RollbackTransactionRequestCodec;
84
+ private constructor();
85
+ get body(): Record<string, unknown>;
86
+ get contentType(): string;
87
+ get accept(): string;
88
+ get authorization(): string;
89
+ }
90
+ export declare class RollbackTransactionResponseCodec {
91
+ static of(config: types.InternalConfig, contentType: string, response: RawRollbackTransactionResponse): RollbackTransactionResponseCodec;
92
+ get error(): Error | undefined;
93
+ get meta(): Record<string, unknown>;
94
+ }
package/types/index.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { auth, BookmarkManager, bookmarkManager, BookmarkManagerConfig, authTokenManagers, AuthTokenManager, AuthTokenManagers, AuthTokenAndExpiration, Connection, ConnectionProvider, Date, DateTime, Driver, Duration, error, int, Integer, internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, isPoint, isRelationship, isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, Neo4jError, Node, Notification, notificationCategory, NotificationCategory, NotificationPosition, notificationSeverityLevel, NotificationSeverityLevel, Path, PathSegment, Plan, Point, ProfiledPlan, QueryConfig, QueryResult, QueryStatistics, Record, RecordShape, Relationship, Result, ResultObserver, ResultSummary, ResultTransformer, resultTransformers, ServerInfo, Session, SessionConfig, Time, types as coreTypes, UnboundRelationship } from 'neo4j-driver-core';
17
+ import { auth, BookmarkManager, bookmarkManager, BookmarkManagerConfig, authTokenManagers, AuthTokenManager, AuthTokenManagers, AuthTokenAndExpiration, Connection, ConnectionProvider, Date, DateTime, Driver, Duration, error, int, Integer, internal, isDate, isDateTime, isDuration, isInt, isLocalDateTime, isLocalTime, isNode, isPath, isPathSegment, isPoint, isRelationship, isRetriableError, isTime, isUnboundRelationship, LocalDateTime, LocalTime, Neo4jError, Node, Notification, notificationCategory, NotificationCategory, NotificationPosition, notificationSeverityLevel, NotificationSeverityLevel, Path, PathSegment, Plan, Point, ProfiledPlan, QueryConfig, QueryResult, QueryStatistics, Record, RecordShape, Relationship, Result, ResultObserver, ResultSummary, ResultTransformer, resultTransformers, ServerInfo, Session, SessionConfig, Time, types as coreTypes, UnboundRelationship, Transaction, TransactionConfig, TransactionPromise, ManagedTransaction } from 'neo4j-driver-core';
18
18
  import { logging } from './logging';
19
19
  import { HttpUrl, Wrapper, WrapperSession, WrapperConfig, WrapperSessionConfig } from './types';
20
20
  type AuthToken = coreTypes.AuthToken;
@@ -191,6 +191,9 @@ declare const forExport: {
191
191
  Result: typeof Result;
192
192
  Record: typeof Record;
193
193
  ResultSummary: typeof ResultSummary;
194
+ Transaction: typeof Transaction;
195
+ TransactionPromise: typeof TransactionPromise;
196
+ ManagedTransaction: typeof ManagedTransaction;
194
197
  Node: typeof Node;
195
198
  Relationship: typeof Relationship;
196
199
  UnboundRelationship: typeof UnboundRelationship;
@@ -248,12 +251,12 @@ declare const forExport: {
248
251
  SCHEMA: "SCHEMA";
249
252
  };
250
253
  notificationSeverityLevel: {
254
+ UNKNOWN: "UNKNOWN";
251
255
  WARNING: "WARNING";
252
256
  INFORMATION: "INFORMATION";
253
- UNKNOWN: "UNKNOWN";
254
257
  };
255
258
  wrapper: typeof wrapper;
256
259
  };
257
- export { authTokenManagers, int, isInt, isPoint, isDuration, isLocalTime, isTime, isDate, isLocalDateTime, isDateTime, isNode, isPath, isPathSegment, isRelationship, isUnboundRelationship, integer, Neo4jError, isRetriableError, auth, logging, types, session, error, graph, spatial, temporal, Driver, Result, Record, ResultSummary, Node, Relationship, UnboundRelationship, PathSegment, Path, Integer, Plan, ProfiledPlan, QueryStatistics, Notification, ServerInfo, Session, Point, Duration, LocalTime, Time, Date, LocalDateTime, DateTime, ConnectionProvider, Connection, bookmarkManager, resultTransformers, notificationCategory, notificationSeverityLevel, wrapper };
258
- export type { QueryResult, AuthToken, AuthTokenManager, AuthTokenManagers, AuthTokenAndExpiration, Config, EncryptionLevel, TrustStrategy, SessionMode, ResultObserver, NotificationPosition, BookmarkManager, BookmarkManagerConfig, SessionConfig, QueryConfig, RecordShape, ResultTransformer, NotificationCategory, NotificationSeverityLevel, Logger, HttpUrl, Wrapper, WrapperConfig, WrapperSession, WrapperSessionConfig };
260
+ export { authTokenManagers, int, isInt, isPoint, isDuration, isLocalTime, isTime, isDate, isLocalDateTime, isDateTime, isNode, isPath, isPathSegment, isRelationship, isUnboundRelationship, integer, Neo4jError, isRetriableError, auth, logging, types, session, error, graph, spatial, temporal, Driver, Result, Record, ResultSummary, Node, Relationship, UnboundRelationship, PathSegment, Path, Integer, Plan, ProfiledPlan, QueryStatistics, Notification, ServerInfo, Session, Transaction, TransactionPromise, ManagedTransaction, Point, Duration, LocalTime, Time, Date, LocalDateTime, DateTime, ConnectionProvider, Connection, bookmarkManager, resultTransformers, notificationCategory, notificationSeverityLevel, wrapper };
261
+ export type { QueryResult, AuthToken, AuthTokenManager, AuthTokenManagers, AuthTokenAndExpiration, Config, EncryptionLevel, TrustStrategy, SessionMode, ResultObserver, NotificationPosition, BookmarkManager, BookmarkManagerConfig, SessionConfig, QueryConfig, RecordShape, ResultTransformer, TransactionConfig, NotificationCategory, NotificationSeverityLevel, Logger, HttpUrl, Wrapper, WrapperConfig, WrapperSession, WrapperSessionConfig };
259
262
  export default forExport;
package/types/types.d.ts CHANGED
@@ -30,12 +30,14 @@ type VerifyAuthentication = {
30
30
  }): Promise<boolean>;
31
31
  };
32
32
  type HttpUrl = `http://${string}` | `https://${string}`;
33
- type WrapperSession = Pick<Session, 'run' | 'lastBookmarks' | 'close'> & Disposable;
33
+ type WrapperSession = Pick<Session, 'run' | 'beginTransaction' | 'executeRead' | 'executeWrite' | 'lastBookmarks' | 'close'> & Disposable;
34
34
  type WrapperSessionConfig = Pick<SessionConfig, 'bookmarks' | 'impersonatedUser' | 'bookmarkManager' | 'defaultAccessMode' | 'auth'> & {
35
35
  database: string;
36
36
  };
37
- type Wrapper = Pick<Driver, 'close' | 'supportsMultiDb' | 'supportsSessionAuth' | 'supportsUserImpersonation'> & Disposable & VerifyConnectivity & VerifyAuthentication & {
37
+ type Wrapper = Pick<Driver, 'close' | 'executeQuery' | 'executeQueryBookmarkManager' | 'supportsMultiDb' | 'supportsSessionAuth' | 'supportsUserImpersonation'> & Disposable & VerifyConnectivity & VerifyAuthentication & {
38
38
  session(config: WrapperSessionConfig): WrapperSession;
39
39
  };
40
- type WrapperConfig = Pick<Config, 'encrypted' | 'useBigInt' | 'disableLosslessIntegers' | 'maxConnectionPoolSize' | 'connectionAcquisitionTimeout'>;
40
+ type WrapperConfig = Pick<Config, 'encrypted' | 'useBigInt' | 'disableLosslessIntegers' | 'maxConnectionPoolSize' | 'connectionAcquisitionTimeout'> & {
41
+ httpSessionAffinityHeader?: string;
42
+ };
41
43
  export type { HttpUrl, WrapperSession, WrapperSessionConfig, Wrapper, WrapperConfig };
@@ -14,7 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { RecordShape, TransactionConfig, Result, Session } from "neo4j-driver-core";
17
+ import { RecordShape, TransactionConfig, Result, Session, TransactionPromise, ManagedTransaction } from "neo4j-driver-core";
18
18
  import { Query } from "neo4j-driver-core/types/types";
19
19
  import { WrapperSession } from "./types";
20
20
  export default class WrapperSessionImpl implements WrapperSession {
@@ -22,6 +22,9 @@ export default class WrapperSessionImpl implements WrapperSession {
22
22
  constructor(session: Session);
23
23
  [Symbol.asyncDispose](): Promise<void>;
24
24
  run<R extends RecordShape = RecordShape>(query: Query, parameters?: any, transactionConfig?: TransactionConfig | undefined): Result<R>;
25
+ beginTransaction(transactionConfig?: TransactionConfig | undefined): TransactionPromise;
26
+ executeRead<T>(transactionWork: (tx: ManagedTransaction) => T | Promise<T>, transactionConfig?: TransactionConfig | undefined): Promise<T>;
27
+ executeWrite<T>(transactionWork: (tx: ManagedTransaction) => T | Promise<T>, transactionConfig?: TransactionConfig | undefined): Promise<T>;
25
28
  lastBookmarks(): string[];
26
29
  close(): Promise<void>;
27
30
  }
@@ -14,12 +14,14 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- import { Driver, ServerInfo, types } from "neo4j-driver-core";
17
+ import { BookmarkManager, Driver, EagerResult, QueryConfig, RecordShape, ServerInfo, types } from "neo4j-driver-core";
18
18
  import { Wrapper, WrapperSession, WrapperSessionConfig } from "./types";
19
19
  export declare class WrapperImpl implements Wrapper {
20
20
  private readonly driver;
21
21
  constructor(driver: Driver);
22
22
  close(): Promise<void>;
23
+ executeQuery<T = EagerResult<RecordShape>>(query: types.Query, parameters?: any, config?: QueryConfig<T> | undefined): Promise<T>;
24
+ get executeQueryBookmarkManager(): BookmarkManager;
23
25
  verifyConnectivity(config: {
24
26
  database: string;
25
27
  }): Promise<ServerInfo>;