gremlin 3.5.1 → 3.5.4
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.
- package/.eslintrc.js +155 -0
- package/.prettierrc.js +29 -0
- package/Gruntfile.js +15 -16
- package/doc/AnonymousTraversalSource.html +5 -5
- package/doc/Authenticator.html +2 -2
- package/doc/Bytecode.html +7 -7
- package/doc/Client.html +138 -64
- package/doc/Connection.html +93 -9
- package/doc/DriverRemoteConnection.html +11 -11
- package/doc/EdgeLabelVerificationStrategy.html +3 -3
- package/doc/Graph.html +2 -2
- package/doc/GraphSON2Reader.html +4 -4
- package/doc/GraphSON2Writer.html +3 -3
- package/doc/GraphSON3Reader.html +3 -3
- package/doc/GraphSON3Writer.html +3 -3
- package/doc/GraphTraversal.html +107 -107
- package/doc/GraphTraversalSource.html +19 -19
- package/doc/HaltedTraverserStrategy.html +3 -3
- package/doc/MatchAlgorithmStrategy.html +3 -3
- package/doc/P.html +13 -13
- package/doc/PartitionStrategy.html +3 -3
- package/doc/Path.html +2 -2
- package/doc/PlainTextSaslAuthenticator.html +2 -2
- package/doc/ProductiveByStrategy.html +288 -0
- package/doc/RemoteConnection.html +10 -10
- package/doc/RemoteStrategy.html +4 -4
- package/doc/RemoteTraversal.html +3 -3
- package/doc/ReservedKeysVerificationStrategy.html +3 -3
- package/doc/ResponseError.html +2 -2
- package/doc/ResultSet.html +7 -7
- package/doc/SaslAuthenticator.html +2 -2
- package/doc/SaslMechanismBase.html +2 -2
- package/doc/SaslMechanismPlain.html +6 -6
- package/doc/SeedStrategy.html +288 -0
- package/doc/SubgraphStrategy.html +3 -3
- package/doc/TextP.html +10 -10
- package/doc/Transaction.html +6 -6
- package/doc/Translator.html +198 -4
- package/doc/TraversalStrategies.html +5 -5
- package/doc/TraversalStrategy.html +3 -3
- package/doc/TypeSerializer.html +2 -2
- package/doc/driver_auth_authenticator.js.html +4 -4
- package/doc/driver_auth_mechanisms_sasl-mechanism-base.js.html +3 -3
- package/doc/driver_auth_mechanisms_sasl-mechanism-plain.js.html +26 -17
- package/doc/driver_auth_plain-text-sasl-authenticator.js.html +7 -7
- package/doc/driver_auth_sasl-authenticator.js.html +3 -3
- package/doc/driver_client.js.html +73 -23
- package/doc/driver_connection.js.html +134 -92
- package/doc/driver_driver-remote-connection.js.html +12 -11
- package/doc/driver_remote-connection.js.html +4 -5
- package/doc/driver_response-error.js.html +5 -4
- package/doc/driver_result-set.js.html +4 -4
- package/doc/global.html +264 -3
- package/doc/index.html +2 -2
- package/doc/module.exports.html +301 -0
- package/doc/process_anonymous-traversal.js.html +4 -4
- package/doc/process_bytecode.js.html +11 -9
- package/doc/process_graph-traversal.js.html +194 -143
- package/doc/process_transaction.js.html +16 -9
- package/doc/process_translator.js.html +53 -37
- package/doc/process_traversal-strategy.js.html +70 -53
- package/doc/process_traversal.js.html +101 -108
- package/doc/structure_graph.js.html +7 -7
- package/doc/structure_io_binary_internals_DataType.js.html +148 -0
- package/doc/structure_io_binary_internals_GraphBinaryReader.js.html +131 -0
- package/doc/structure_io_binary_internals_GraphBinaryWriter.js.html +134 -0
- package/doc/structure_io_graph-serializer.js.html +65 -18
- package/doc/structure_io_type-serializers.js.html +86 -89
- package/index.js +5 -5
- package/lib/driver/auth/authenticator.js +2 -2
- package/lib/driver/auth/mechanisms/sasl-mechanism-base.js +1 -1
- package/lib/driver/auth/mechanisms/sasl-mechanism-plain.js +24 -15
- package/lib/driver/auth/plain-text-sasl-authenticator.js +5 -5
- package/lib/driver/auth/sasl-authenticator.js +1 -1
- package/lib/driver/client.js +71 -21
- package/lib/driver/connection.js +132 -90
- package/lib/driver/driver-remote-connection.js +10 -9
- package/lib/driver/remote-connection.js +2 -3
- package/lib/driver/response-error.js +2 -2
- package/lib/driver/result-set.js +1 -2
- package/lib/process/anonymous-traversal.js +1 -2
- package/lib/process/bytecode.js +9 -7
- package/lib/process/graph-traversal.js +191 -141
- package/lib/process/transaction.js +13 -7
- package/lib/process/translator.js +50 -35
- package/lib/process/traversal-strategy.js +67 -51
- package/lib/process/traversal.js +99 -106
- package/lib/structure/graph.js +5 -5
- package/lib/structure/io/binary/GraphBinary.js +109 -0
- package/lib/structure/io/binary/internals/AnySerializer.js +95 -0
- package/lib/structure/io/binary/internals/ArraySerializer.js +135 -0
- package/lib/structure/io/binary/internals/BigIntegerSerializer.js +151 -0
- package/lib/structure/io/binary/internals/BooleanSerializer.js +103 -0
- package/lib/structure/io/binary/internals/BulkSetSerializer.js +124 -0
- package/lib/structure/io/binary/internals/ByteBufferSerializer.js +124 -0
- package/lib/structure/io/binary/internals/ByteSerializer.js +95 -0
- package/lib/structure/io/binary/internals/BytecodeSerializer.js +249 -0
- package/lib/structure/io/binary/internals/DataType.js +97 -0
- package/lib/structure/io/binary/internals/DateSerializer.js +103 -0
- package/lib/structure/io/binary/internals/DoubleSerializer.js +99 -0
- package/lib/structure/io/binary/internals/EdgeSerializer.js +215 -0
- package/lib/structure/io/binary/internals/EnumSerializer.js +148 -0
- package/lib/structure/io/binary/internals/FloatSerializer.js +99 -0
- package/lib/structure/io/binary/internals/GraphBinaryReader.js +80 -0
- package/lib/structure/io/binary/internals/GraphBinaryWriter.js +83 -0
- package/lib/structure/io/binary/internals/IntSerializer.js +112 -0
- package/lib/structure/io/binary/internals/LambdaSerializer.js +70 -0
- package/lib/structure/io/binary/internals/LongSerializer.js +107 -0
- package/lib/structure/io/binary/internals/LongSerializerNg.js +102 -0
- package/lib/structure/io/binary/internals/MapSerializer.js +153 -0
- package/lib/structure/io/binary/internals/NumberSerializationStrategy.js +68 -0
- package/lib/structure/io/binary/internals/PSerializer.js +151 -0
- package/lib/structure/io/binary/internals/PathSerializer.js +123 -0
- package/lib/structure/io/binary/internals/PropertySerializer.js +141 -0
- package/lib/structure/io/binary/internals/ShortSerializer.js +99 -0
- package/lib/structure/io/binary/internals/StringSerializer.js +114 -0
- package/lib/structure/io/binary/internals/TextPSerializer.js +145 -0
- package/lib/structure/io/binary/internals/TraversalStrategySerializer.js +72 -0
- package/lib/structure/io/binary/internals/TraverserSerializer.js +123 -0
- package/lib/structure/io/binary/internals/UnspecifiedNullSerializer.js +75 -0
- package/lib/structure/io/binary/internals/UuidSerializer.js +119 -0
- package/lib/structure/io/binary/internals/VertexPropertySerializer.js +172 -0
- package/lib/structure/io/binary/internals/VertexSerializer.js +135 -0
- package/lib/structure/io/binary/internals/utils.js +59 -0
- package/lib/structure/io/graph-serializer.js +62 -16
- package/lib/structure/io/type-serializers.js +84 -87
- package/lib/utils.js +15 -10
- package/package.json +21 -8
package/lib/driver/client.js
CHANGED
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
* distributed with this work for additional information
|
|
5
5
|
* regarding copyright ownership. The ASF licenses this file
|
|
6
6
|
* to you under the Apache License, Version 2.0 (the
|
|
7
|
-
*
|
|
7
|
+
* 'License'); you may not use this file except in compliance
|
|
8
8
|
* with the License. You may obtain a copy of the License at
|
|
9
9
|
*
|
|
10
10
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
11
|
*
|
|
12
12
|
* Unless required by applicable law or agreed to in writing,
|
|
13
13
|
* software distributed under the License is distributed on an
|
|
14
|
-
*
|
|
14
|
+
* 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
15
|
* KIND, either express or implied. See the License for the
|
|
16
16
|
* specific language governing permissions and limitations
|
|
17
17
|
* under the License.
|
|
@@ -26,7 +26,6 @@ const Bytecode = require('../process/bytecode');
|
|
|
26
26
|
* A {@link Client} contains methods to send messages to a Gremlin Server.
|
|
27
27
|
*/
|
|
28
28
|
class Client {
|
|
29
|
-
|
|
30
29
|
/**
|
|
31
30
|
* Creates a new instance of {@link Client}.
|
|
32
31
|
* @param {String} url The resource uri.
|
|
@@ -73,25 +72,35 @@ class Client {
|
|
|
73
72
|
return this._connection.isOpen;
|
|
74
73
|
}
|
|
75
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Configuration specific to the current request.
|
|
77
|
+
* @typedef {Object} RequestOptions
|
|
78
|
+
* @property {String} requestId - User specified request identifier which must be a UUID.
|
|
79
|
+
* @property {Number} batchSize - Indicates whether the Power component is present.
|
|
80
|
+
* @property {String} userAgent - The size in which the result of a request is to be 'batched' back to the client
|
|
81
|
+
* @property {Number} evaluationTimeout - The timeout for the evaluation of the request.
|
|
82
|
+
*/
|
|
83
|
+
|
|
76
84
|
/**
|
|
77
85
|
* Send a request to the Gremlin Server, can send a script or bytecode steps.
|
|
78
86
|
* @param {Bytecode|string} message The bytecode or script to send
|
|
79
87
|
* @param {Object} [bindings] The script bindings, if any.
|
|
80
|
-
* @param {
|
|
81
|
-
* @param {String} [requestOptions.requestId] User specified request identifier which must be a UUID.
|
|
82
|
-
* @param {Number} [requestOptions.batchSize] The size in which the result of a request is to be "batched" back to the client
|
|
83
|
-
* @param {String} [requestOptions.userAgent] A custom string that specifies to the server where the request came from.
|
|
84
|
-
* @param {Number} [requestOptions.evaluationTimeout] The timeout for the evaluation of the request.
|
|
88
|
+
* @param {RequestOptions} [requestOptions] Configuration specific to the current request.
|
|
85
89
|
* @returns {Promise}
|
|
86
90
|
*/
|
|
87
91
|
submit(message, bindings, requestOptions) {
|
|
88
|
-
const requestIdOverride = requestOptions && requestOptions.requestId
|
|
89
|
-
if (requestIdOverride)
|
|
92
|
+
const requestIdOverride = requestOptions && requestOptions.requestId;
|
|
93
|
+
if (requestIdOverride) {
|
|
94
|
+
delete requestOptions['requestId'];
|
|
95
|
+
}
|
|
90
96
|
|
|
91
|
-
const args = Object.assign(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
const args = Object.assign(
|
|
98
|
+
{
|
|
99
|
+
gremlin: message,
|
|
100
|
+
aliases: { g: this._options.traversalSource || 'g' },
|
|
101
|
+
},
|
|
102
|
+
requestOptions,
|
|
103
|
+
);
|
|
95
104
|
|
|
96
105
|
if (this._options.session && this._options.processor === 'session') {
|
|
97
106
|
args['session'] = this._options.session;
|
|
@@ -100,17 +109,54 @@ class Client {
|
|
|
100
109
|
if (message instanceof Bytecode) {
|
|
101
110
|
if (this._options.session && this._options.processor === 'session') {
|
|
102
111
|
return this._connection.submit('session', 'bytecode', args, requestIdOverride);
|
|
103
|
-
} else {
|
|
104
|
-
return this._connection.submit('traversal', 'bytecode', args, requestIdOverride);
|
|
105
112
|
}
|
|
113
|
+
return this._connection.submit('traversal', 'bytecode', args, requestIdOverride);
|
|
114
|
+
} else if (typeof message === 'string') {
|
|
115
|
+
args['bindings'] = bindings;
|
|
116
|
+
args['language'] = 'gremlin-groovy';
|
|
117
|
+
args['accept'] = this._connection.mimeType;
|
|
118
|
+
return this._connection.submit(this._options.processor || '', 'eval', args, requestIdOverride);
|
|
119
|
+
}
|
|
120
|
+
throw new TypeError('message must be of type Bytecode or string');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Send a request to the Gremlin Server and receive a stream for the results, can send a script or bytecode steps.
|
|
125
|
+
* @param {Bytecode|string} message The bytecode or script to send
|
|
126
|
+
* @param {Object} [bindings] The script bindings, if any.
|
|
127
|
+
* @param {RequestOptions} [requestOptions] Configuration specific to the current request.
|
|
128
|
+
* @returns {ReadableStream}
|
|
129
|
+
*/
|
|
130
|
+
stream(message, bindings, requestOptions) {
|
|
131
|
+
const requestIdOverride = requestOptions && requestOptions.requestId;
|
|
132
|
+
if (requestIdOverride) {
|
|
133
|
+
delete requestOptions['requestId'];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const args = Object.assign(
|
|
137
|
+
{
|
|
138
|
+
gremlin: message,
|
|
139
|
+
aliases: { g: this._options.traversalSource || 'g' },
|
|
140
|
+
},
|
|
141
|
+
requestOptions,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
if (this._options.session && this._options.processor === 'session') {
|
|
145
|
+
args['session'] = this._options.session;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (message instanceof Bytecode) {
|
|
149
|
+
if (this._options.session && this._options.processor === 'session') {
|
|
150
|
+
return this._connection.stream('session', 'bytecode', args, requestIdOverride);
|
|
151
|
+
}
|
|
152
|
+
return this._connection.stream('traversal', 'bytecode', args, requestIdOverride);
|
|
106
153
|
} else if (typeof message === 'string') {
|
|
107
154
|
args['bindings'] = bindings;
|
|
108
155
|
args['language'] = 'gremlin-groovy';
|
|
109
156
|
args['accept'] = this._connection.mimeType;
|
|
110
|
-
return this._connection.
|
|
111
|
-
} else {
|
|
112
|
-
throw new TypeError("message must be of type Bytecode or string");
|
|
157
|
+
return this._connection.stream(this._options.processor || '', 'eval', args, requestIdOverride);
|
|
113
158
|
}
|
|
159
|
+
throw new TypeError('message must be of type Bytecode or string');
|
|
114
160
|
}
|
|
115
161
|
|
|
116
162
|
/**
|
|
@@ -119,6 +165,10 @@ class Client {
|
|
|
119
165
|
* @returns {Promise}
|
|
120
166
|
*/
|
|
121
167
|
close() {
|
|
168
|
+
if (this._options.session && this._options.processor === 'session') {
|
|
169
|
+
const args = { session: this._options.session };
|
|
170
|
+
return this._connection.submit(this._options.processor, 'close', args, null).then(() => this._connection.close());
|
|
171
|
+
}
|
|
122
172
|
return this._connection.close();
|
|
123
173
|
}
|
|
124
174
|
|
|
@@ -128,7 +178,7 @@ class Client {
|
|
|
128
178
|
* @param {Function} handler The callback to be called when the event occurs.
|
|
129
179
|
*/
|
|
130
180
|
addListener(event, handler) {
|
|
131
|
-
this._connection.on(event, handler)
|
|
181
|
+
this._connection.on(event, handler);
|
|
132
182
|
}
|
|
133
183
|
|
|
134
184
|
/**
|
|
@@ -137,7 +187,7 @@ class Client {
|
|
|
137
187
|
* @param {Function} handler The event handler to be removed.
|
|
138
188
|
*/
|
|
139
189
|
removeListener(event, handler) {
|
|
140
|
-
this._connection.removeListener(event, handler)
|
|
190
|
+
this._connection.removeListener(event, handler);
|
|
141
191
|
}
|
|
142
192
|
}
|
|
143
193
|
|
package/lib/driver/connection.js
CHANGED
|
@@ -23,23 +23,25 @@
|
|
|
23
23
|
'use strict';
|
|
24
24
|
|
|
25
25
|
const EventEmitter = require('events');
|
|
26
|
+
const Stream = require('stream');
|
|
26
27
|
const WebSocket = require('ws');
|
|
27
28
|
const util = require('util');
|
|
28
29
|
const utils = require('../utils');
|
|
29
30
|
const serializer = require('../structure/io/graph-serializer');
|
|
31
|
+
const { graphBinaryReader, graphBinaryWriter } = require('../structure/io/binary/GraphBinary');
|
|
30
32
|
const ResultSet = require('./result-set');
|
|
31
33
|
const ResponseError = require('./response-error');
|
|
32
|
-
const Bytecode = require('../process/bytecode');
|
|
33
34
|
|
|
34
35
|
const responseStatusCode = {
|
|
35
36
|
success: 200,
|
|
36
37
|
noContent: 204,
|
|
37
38
|
partialContent: 206,
|
|
38
|
-
authenticationChallenge:
|
|
39
|
+
authenticationChallenge: 407,
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
const defaultMimeType = 'application/vnd.gremlin-v3.0+json';
|
|
42
43
|
const graphSON2MimeType = 'application/vnd.gremlin-v2.0+json';
|
|
44
|
+
const graphBinaryMimeType = 'application/vnd.graphbinary-v1.0';
|
|
43
45
|
|
|
44
46
|
const pingIntervalDelay = 60 * 1000;
|
|
45
47
|
const pongTimeoutDelay = 30 * 1000;
|
|
@@ -48,7 +50,6 @@ const pongTimeoutDelay = 30 * 1000;
|
|
|
48
50
|
* Represents a single connection to a Gremlin Server.
|
|
49
51
|
*/
|
|
50
52
|
class Connection extends EventEmitter {
|
|
51
|
-
|
|
52
53
|
/**
|
|
53
54
|
* Creates a new instance of {@link Connection}.
|
|
54
55
|
* @param {String} url The resource uri.
|
|
@@ -66,7 +67,7 @@ class Connection extends EventEmitter {
|
|
|
66
67
|
* @param {Boolean} [options.pingEnabled] Setup ping interval. Defaults to: true.
|
|
67
68
|
* @param {Number} [options.pingInterval] Ping request interval in ms if ping enabled. Defaults to: 60000.
|
|
68
69
|
* @param {Number} [options.pongTimeout] Timeout of pong response in ms after sending a ping. Defaults to: 30000.
|
|
69
|
-
* @param {Boolean} [options.connectOnStartup] Open websocket on startup.
|
|
70
|
+
* @param {Boolean} [options.connectOnStartup] Deprecated and non-functional. Open websocket on startup.
|
|
70
71
|
* @constructor
|
|
71
72
|
*/
|
|
72
73
|
constructor(url, options) {
|
|
@@ -92,7 +93,8 @@ class Connection extends EventEmitter {
|
|
|
92
93
|
this._pingInterval = null;
|
|
93
94
|
this._pongTimeout = null;
|
|
94
95
|
|
|
95
|
-
this._header = String.fromCharCode(this.mimeType.length) + this.mimeType;
|
|
96
|
+
this._header = String.fromCharCode(this.mimeType.length) + this.mimeType; // TODO: what if mimeType.length > 255
|
|
97
|
+
this._header_buf = Buffer.from(this._header);
|
|
96
98
|
this.isOpen = false;
|
|
97
99
|
this.traversalSource = options.traversalSource || 'g';
|
|
98
100
|
this._authenticator = options.authenticator;
|
|
@@ -101,8 +103,10 @@ class Connection extends EventEmitter {
|
|
|
101
103
|
this._pingIntervalDelay = this.options.pingInterval || pingIntervalDelay;
|
|
102
104
|
this._pongTimeoutDelay = this.options.pongTimeout || pongTimeoutDelay;
|
|
103
105
|
|
|
104
|
-
if (this.options.connectOnStartup
|
|
105
|
-
|
|
106
|
+
if (this.options.connectOnStartup) {
|
|
107
|
+
console.warn(
|
|
108
|
+
'connectOnStartup is now deprecated and non-functional. To open a connection, please call open() after instantiating connection object.',
|
|
109
|
+
);
|
|
106
110
|
}
|
|
107
111
|
}
|
|
108
112
|
|
|
@@ -118,14 +122,14 @@ class Connection extends EventEmitter {
|
|
|
118
122
|
return this._openPromise;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
this.emit('log',
|
|
125
|
+
this.emit('log', 'ws open');
|
|
122
126
|
|
|
123
127
|
this._ws = new WebSocket(this.url, {
|
|
124
128
|
headers: this.options.headers,
|
|
125
129
|
ca: this.options.ca,
|
|
126
130
|
cert: this.options.cert,
|
|
127
131
|
pfx: this.options.pfx,
|
|
128
|
-
rejectUnauthorized: this.options.rejectUnauthorized
|
|
132
|
+
rejectUnauthorized: this.options.rejectUnauthorized,
|
|
129
133
|
});
|
|
130
134
|
|
|
131
135
|
this._ws.on('message', (data) => this._handleMessage(data));
|
|
@@ -143,7 +147,7 @@ class Connection extends EventEmitter {
|
|
|
143
147
|
this._ws.pong();
|
|
144
148
|
});
|
|
145
149
|
|
|
146
|
-
return this._openPromise = new Promise((resolve, reject) => {
|
|
150
|
+
return (this._openPromise = new Promise((resolve, reject) => {
|
|
147
151
|
this._ws.on('open', () => {
|
|
148
152
|
this.isOpen = true;
|
|
149
153
|
if (this._pingEnabled) {
|
|
@@ -155,59 +159,87 @@ class Connection extends EventEmitter {
|
|
|
155
159
|
this._handleError(err);
|
|
156
160
|
reject(err);
|
|
157
161
|
});
|
|
158
|
-
});
|
|
162
|
+
}));
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
/** @override */
|
|
162
166
|
submit(processor, op, args, requestId) {
|
|
163
167
|
const rid = requestId || utils.getUuid();
|
|
164
|
-
return this.open().then(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
return this.open().then(
|
|
169
|
+
() =>
|
|
170
|
+
new Promise((resolve, reject) => {
|
|
171
|
+
if (op !== 'authentication') {
|
|
172
|
+
this._responseHandlers[rid] = {
|
|
173
|
+
callback: (err, result) => (err ? reject(err) : resolve(result)),
|
|
174
|
+
result: null,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const request = {
|
|
179
|
+
requestId: rid,
|
|
180
|
+
op: op || 'bytecode',
|
|
181
|
+
// if using op eval need to ensure processor stays unset if caller didn't set it.
|
|
182
|
+
processor: !processor && op !== 'eval' ? 'traversal' : processor,
|
|
183
|
+
args: args || {},
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const request_buf = this._writer.writeRequest(request);
|
|
187
|
+
const message = Buffer.concat([this._header_buf, request_buf]);
|
|
188
|
+
this._ws.send(message);
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/** @override */
|
|
194
|
+
stream(processor, op, args, requestId) {
|
|
195
|
+
const rid = requestId || utils.getUuid();
|
|
196
|
+
|
|
197
|
+
const readableStream = new Stream.Readable({
|
|
198
|
+
objectMode: true,
|
|
199
|
+
read() {},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
this._responseHandlers[rid] = {
|
|
203
|
+
callback: (err) => (err ? readableStream.destroy(err) : readableStream.push(null)),
|
|
204
|
+
result: readableStream,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
this.open()
|
|
208
|
+
.then(() => {
|
|
209
|
+
const request = {
|
|
210
|
+
requestId: rid,
|
|
211
|
+
op: op || 'bytecode',
|
|
212
|
+
// if using op eval need to ensure processor stays unset if caller didn't set it.
|
|
213
|
+
processor: !processor && op !== 'eval' ? 'traversal' : processor,
|
|
214
|
+
args: args || {},
|
|
169
215
|
};
|
|
170
|
-
}
|
|
171
216
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
217
|
+
const request_buf = this._writer.writeRequest(request);
|
|
218
|
+
const message = Buffer.concat([this._header_buf, request_buf]);
|
|
219
|
+
this._ws.send(message);
|
|
220
|
+
})
|
|
221
|
+
.catch((err) => readableStream.destroy(err));
|
|
222
|
+
|
|
223
|
+
return readableStream;
|
|
175
224
|
}
|
|
176
225
|
|
|
177
226
|
_getDefaultReader(mimeType) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
227
|
+
if (mimeType === graphBinaryMimeType) {
|
|
228
|
+
return graphBinaryReader;
|
|
229
|
+
}
|
|
182
230
|
|
|
183
|
-
|
|
184
|
-
return mimeType === graphSON2MimeType
|
|
185
|
-
? new serializer.GraphSON2Writer()
|
|
186
|
-
: new serializer.GraphSONWriter();
|
|
231
|
+
return mimeType === graphSON2MimeType ? new serializer.GraphSON2Reader() : new serializer.GraphSONReader();
|
|
187
232
|
}
|
|
188
233
|
|
|
189
|
-
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
} else {
|
|
193
|
-
args = {};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
if (args['gremlin'] instanceof Bytecode) {
|
|
197
|
-
args['gremlin'] = this._writer.adaptObject(args['gremlin']);
|
|
234
|
+
_getDefaultWriter(mimeType) {
|
|
235
|
+
if (mimeType === graphBinaryMimeType) {
|
|
236
|
+
return graphBinaryWriter;
|
|
198
237
|
}
|
|
199
238
|
|
|
200
|
-
return (
|
|
201
|
-
'requestId': { '@type': 'g:UUID', '@value': id },
|
|
202
|
-
'op': op || 'bytecode',
|
|
203
|
-
// if using op eval need to ensure processor stays unset if caller didn't set it.
|
|
204
|
-
'processor': (!processor && op !== 'eval') ? 'traversal' : processor,
|
|
205
|
-
'args': args
|
|
206
|
-
});
|
|
239
|
+
return mimeType === graphSON2MimeType ? new serializer.GraphSON2Writer() : new serializer.GraphSONWriter();
|
|
207
240
|
}
|
|
208
241
|
|
|
209
242
|
_pingHeartbeat() {
|
|
210
|
-
|
|
211
243
|
if (this._pingInterval) {
|
|
212
244
|
clearInterval(this._pingInterval);
|
|
213
245
|
this._pingInterval = null;
|
|
@@ -227,13 +259,12 @@ class Connection extends EventEmitter {
|
|
|
227
259
|
}, this._pongTimeoutDelay);
|
|
228
260
|
|
|
229
261
|
this._ws.ping();
|
|
230
|
-
|
|
231
262
|
}, this._pingIntervalDelay);
|
|
232
263
|
}
|
|
233
264
|
|
|
234
265
|
_handleError(err) {
|
|
235
266
|
this.emit('log', `ws error ${err}`);
|
|
236
|
-
this._cleanupWebsocket();
|
|
267
|
+
this._cleanupWebsocket(err);
|
|
237
268
|
this.emit('socketError', err);
|
|
238
269
|
}
|
|
239
270
|
|
|
@@ -247,22 +278,30 @@ class Connection extends EventEmitter {
|
|
|
247
278
|
}
|
|
248
279
|
|
|
249
280
|
_handleMessage(data) {
|
|
250
|
-
const response = this._reader.
|
|
281
|
+
const response = this._reader.readResponse(data);
|
|
251
282
|
if (response.requestId === null || response.requestId === undefined) {
|
|
252
283
|
// There was a serialization issue on the server that prevented the parsing of the request id
|
|
253
284
|
// We invoke any of the pending handlers with an error
|
|
254
|
-
Object.keys(this._responseHandlers).forEach(requestId => {
|
|
285
|
+
Object.keys(this._responseHandlers).forEach((requestId) => {
|
|
255
286
|
const handler = this._responseHandlers[requestId];
|
|
256
287
|
this._clearHandler(requestId);
|
|
257
288
|
if (response.status !== undefined && response.status.message) {
|
|
258
289
|
return handler.callback(
|
|
259
290
|
// TINKERPOP-2285: keep the old server error message in case folks are parsing that - fix in a future breaking version
|
|
260
|
-
new ResponseError(
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
291
|
+
new ResponseError(
|
|
292
|
+
util.format(
|
|
293
|
+
'Server error (no request information): %s (%d)',
|
|
294
|
+
response.status.message,
|
|
295
|
+
response.status.code,
|
|
296
|
+
),
|
|
297
|
+
response.status,
|
|
298
|
+
),
|
|
299
|
+
);
|
|
265
300
|
}
|
|
301
|
+
// TINKERPOP-2285: keep the old server error message in case folks are parsing that - fix in a future breaking version
|
|
302
|
+
return handler.callback(
|
|
303
|
+
new ResponseError(util.format('Server error (no request information): %j', response), response.status),
|
|
304
|
+
);
|
|
266
305
|
});
|
|
267
306
|
return;
|
|
268
307
|
}
|
|
@@ -276,31 +315,49 @@ class Connection extends EventEmitter {
|
|
|
276
315
|
}
|
|
277
316
|
|
|
278
317
|
if (response.status.code === responseStatusCode.authenticationChallenge && this._authenticator) {
|
|
279
|
-
this._authenticator
|
|
280
|
-
|
|
281
|
-
|
|
318
|
+
this._authenticator
|
|
319
|
+
.evaluateChallenge(response.result.data)
|
|
320
|
+
.then((res) => this.submit(undefined, 'authentication', res, response.requestId))
|
|
321
|
+
.catch(handler.callback);
|
|
282
322
|
|
|
283
323
|
return;
|
|
284
|
-
}
|
|
285
|
-
else if (response.status.code >= 400) {
|
|
324
|
+
} else if (response.status.code >= 400) {
|
|
286
325
|
// callback in error
|
|
287
326
|
return handler.callback(
|
|
288
327
|
// TINKERPOP-2285: keep the old server error message in case folks are parsing that - fix in a future breaking version
|
|
289
|
-
new ResponseError(
|
|
328
|
+
new ResponseError(
|
|
329
|
+
util.format('Server error: %s (%d)', response.status.message, response.status.code),
|
|
330
|
+
response.status,
|
|
331
|
+
),
|
|
332
|
+
);
|
|
290
333
|
}
|
|
334
|
+
|
|
335
|
+
const isStreamingResponse = handler.result instanceof Stream.Readable;
|
|
336
|
+
|
|
291
337
|
switch (response.status.code) {
|
|
292
338
|
case responseStatusCode.noContent:
|
|
293
339
|
this._clearHandler(response.requestId);
|
|
340
|
+
if (isStreamingResponse) {
|
|
341
|
+
handler.result.push(new ResultSet(utils.emptyArray, response.status.attributes));
|
|
342
|
+
return handler.callback(null);
|
|
343
|
+
}
|
|
294
344
|
return handler.callback(null, new ResultSet(utils.emptyArray, response.status.attributes));
|
|
295
345
|
case responseStatusCode.partialContent:
|
|
346
|
+
if (isStreamingResponse) {
|
|
347
|
+
handler.result.push(new ResultSet(response.result.data, response.status.attributes));
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
296
350
|
handler.result = handler.result || [];
|
|
297
351
|
handler.result.push.apply(handler.result, response.result.data);
|
|
298
352
|
break;
|
|
299
353
|
default:
|
|
354
|
+
if (isStreamingResponse) {
|
|
355
|
+
handler.result.push(new ResultSet(response.result.data, response.status.attributes));
|
|
356
|
+
return handler.callback(null);
|
|
357
|
+
}
|
|
300
358
|
if (handler.result) {
|
|
301
359
|
handler.result.push.apply(handler.result, response.result.data);
|
|
302
|
-
}
|
|
303
|
-
else {
|
|
360
|
+
} else {
|
|
304
361
|
handler.result = response.result.data;
|
|
305
362
|
}
|
|
306
363
|
this._clearHandler(response.requestId);
|
|
@@ -311,7 +368,7 @@ class Connection extends EventEmitter {
|
|
|
311
368
|
/**
|
|
312
369
|
* clean websocket context
|
|
313
370
|
*/
|
|
314
|
-
_cleanupWebsocket() {
|
|
371
|
+
_cleanupWebsocket(err) {
|
|
315
372
|
if (this._pingInterval) {
|
|
316
373
|
clearInterval(this._pingInterval);
|
|
317
374
|
}
|
|
@@ -321,6 +378,17 @@ class Connection extends EventEmitter {
|
|
|
321
378
|
}
|
|
322
379
|
this._pongTimeout = null;
|
|
323
380
|
|
|
381
|
+
// Invoke waiting callbacks to complete Promises when closing the websocket
|
|
382
|
+
Object.keys(this._responseHandlers).forEach((requestId) => {
|
|
383
|
+
const handler = this._responseHandlers[requestId];
|
|
384
|
+
const isStreamingResponse = handler.result instanceof Stream.Readable;
|
|
385
|
+
if (isStreamingResponse) {
|
|
386
|
+
handler.callback(null);
|
|
387
|
+
} else {
|
|
388
|
+
const cause = err ? err : new Error('Connection has been closed.');
|
|
389
|
+
handler.callback(cause);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
324
392
|
this._ws.removeAllListeners();
|
|
325
393
|
this._openPromise = null;
|
|
326
394
|
this._closePromise = null;
|
|
@@ -336,32 +404,6 @@ class Connection extends EventEmitter {
|
|
|
336
404
|
delete this._responseHandlers[requestId];
|
|
337
405
|
}
|
|
338
406
|
|
|
339
|
-
/**
|
|
340
|
-
* Takes the given args map and ensures all arguments are passed through to _write.adaptObject
|
|
341
|
-
* @param {Object} args Map of arguments to process.
|
|
342
|
-
* @param {Boolean} protocolLevel Determines whether it's a protocol level binding.
|
|
343
|
-
* @returns {Object}
|
|
344
|
-
* @private
|
|
345
|
-
*/
|
|
346
|
-
_adaptArgs(args, protocolLevel) {
|
|
347
|
-
if (args instanceof Object) {
|
|
348
|
-
let newObj = {};
|
|
349
|
-
Object.keys(args).forEach((key) => {
|
|
350
|
-
// bindings key (at the protocol-level needs special handling. without this, it wraps the generated Map
|
|
351
|
-
// in another map for types like EnumValue. Could be a nicer way to do this but for now it's solving the
|
|
352
|
-
// problem with script submission of non JSON native types
|
|
353
|
-
if (protocolLevel && key === 'bindings')
|
|
354
|
-
newObj[key] = this._adaptArgs(args[key], false);
|
|
355
|
-
else
|
|
356
|
-
newObj[key] = this._writer.adaptObject(args[key]);
|
|
357
|
-
});
|
|
358
|
-
|
|
359
|
-
return newObj;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return args;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
407
|
/**
|
|
366
408
|
* Closes the Connection.
|
|
367
409
|
* @return {Promise}
|
|
@@ -371,7 +413,7 @@ class Connection extends EventEmitter {
|
|
|
371
413
|
return Promise.resolve();
|
|
372
414
|
}
|
|
373
415
|
if (!this._closePromise) {
|
|
374
|
-
this._closePromise = new Promise(resolve => {
|
|
416
|
+
this._closePromise = new Promise((resolve) => {
|
|
375
417
|
this._closeCallback = resolve;
|
|
376
418
|
this._ws.close();
|
|
377
419
|
});
|
|
@@ -34,7 +34,6 @@ const OptionsStrategy = require('../process/traversal-strategy').OptionsStrategy
|
|
|
34
34
|
* Represents the default {@link RemoteConnection} implementation.
|
|
35
35
|
*/
|
|
36
36
|
class DriverRemoteConnection extends RemoteConnection {
|
|
37
|
-
|
|
38
37
|
/**
|
|
39
38
|
* Creates a new instance of {@link DriverRemoteConnection}.
|
|
40
39
|
* @param {String} url The resource uri.
|
|
@@ -68,28 +67,30 @@ class DriverRemoteConnection extends RemoteConnection {
|
|
|
68
67
|
|
|
69
68
|
/** @override */
|
|
70
69
|
submit(bytecode) {
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
const optionsStrategy = bytecode.sourceInstructions.find(
|
|
71
|
+
(i) => i[0] === 'withStrategies' && i[1] instanceof OptionsStrategy,
|
|
72
|
+
);
|
|
73
73
|
const allowedKeys = ['evaluationTimeout', 'scriptEvaluationTimeout', 'batchSize', 'requestId', 'userAgent'];
|
|
74
74
|
|
|
75
75
|
let requestOptions = undefined;
|
|
76
76
|
if (optionsStrategy !== undefined) {
|
|
77
77
|
requestOptions = {};
|
|
78
78
|
const conf = optionsStrategy[1].configuration;
|
|
79
|
-
for (
|
|
79
|
+
for (const key in conf) {
|
|
80
80
|
if (conf.hasOwnProperty(key) && allowedKeys.indexOf(key) > -1) {
|
|
81
81
|
requestOptions[key] = conf[key];
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
return this._client.submit(bytecode, null, requestOptions).then(result => new RemoteTraversal(result.toArray()));
|
|
86
|
+
return this._client.submit(bytecode, null, requestOptions).then((result) => new RemoteTraversal(result.toArray()));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
/** @override */
|
|
90
90
|
createSession() {
|
|
91
|
-
if (this.isSessionBound)
|
|
92
|
-
throw new Error(
|
|
91
|
+
if (this.isSessionBound) {
|
|
92
|
+
throw new Error('Connection is already bound to a session - child sessions are not allowed');
|
|
93
|
+
}
|
|
93
94
|
|
|
94
95
|
// make sure a fresh session is used when starting a new transaction
|
|
95
96
|
const copiedOptions = Object.assign({}, this.options);
|
|
@@ -104,12 +105,12 @@ class DriverRemoteConnection extends RemoteConnection {
|
|
|
104
105
|
|
|
105
106
|
/** @override */
|
|
106
107
|
commit() {
|
|
107
|
-
return this._client.submit(Bytecode.GraphOp.commit, null)
|
|
108
|
+
return this._client.submit(Bytecode.GraphOp.commit, null);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
/** @override */
|
|
111
112
|
rollback() {
|
|
112
|
-
return this._client.submit(Bytecode.GraphOp.rollback, null)
|
|
113
|
+
return this._client.submit(Bytecode.GraphOp.rollback, null);
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
/** @override */
|
|
@@ -30,7 +30,6 @@ const TraversalStrategy = require('../process/traversal-strategy').TraversalStra
|
|
|
30
30
|
* returning results.
|
|
31
31
|
*/
|
|
32
32
|
class RemoteConnection {
|
|
33
|
-
|
|
34
33
|
/**
|
|
35
34
|
* @param {String} url The resource uri.
|
|
36
35
|
* @param {Object} [options] The connection options.
|
|
@@ -73,7 +72,7 @@ class RemoteConnection {
|
|
|
73
72
|
*/
|
|
74
73
|
submit(bytecode) {
|
|
75
74
|
throw new Error('submit() must be implemented');
|
|
76
|
-
}
|
|
75
|
+
}
|
|
77
76
|
|
|
78
77
|
/**
|
|
79
78
|
* Create a new <code>RemoteConnection</code> that is bound to a session using the configuration from this one.
|
|
@@ -128,7 +127,7 @@ class RemoteStrategy extends TraversalStrategy {
|
|
|
128
127
|
// gave this a fqcn that has a local "js:" prefix since this strategy isn't sent as bytecode to the server.
|
|
129
128
|
// this is a sort of local-only strategy that actually executes client side. not sure if this prefix is the
|
|
130
129
|
// right way to name this or not, but it should have a name to identify it.
|
|
131
|
-
super(
|
|
130
|
+
super('js:RemoteStrategy');
|
|
132
131
|
this.connection = connection;
|
|
133
132
|
}
|
|
134
133
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
class ResponseError extends Error {
|
|
26
26
|
constructor(message, responseStatus) {
|
|
27
27
|
super(message);
|
|
28
|
-
this.name =
|
|
28
|
+
this.name = 'ResponseError';
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Gets the server status code.
|
|
@@ -44,4 +44,4 @@ class ResponseError extends Error {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
module.exports = ResponseError;
|
|
47
|
+
module.exports = ResponseError;
|
package/lib/driver/result-set.js
CHANGED
|
@@ -31,7 +31,6 @@ const emptyMap = Object.freeze(new utils.ImmutableMap());
|
|
|
31
31
|
* Represents the response returned from the execution of a Gremlin traversal or script.
|
|
32
32
|
*/
|
|
33
33
|
class ResultSet {
|
|
34
|
-
|
|
35
34
|
/**
|
|
36
35
|
* Creates a new instance of {@link ResultSet}.
|
|
37
36
|
* @param {Array} items
|
|
@@ -90,4 +89,4 @@ class ResultSet {
|
|
|
90
89
|
}
|
|
91
90
|
}
|
|
92
91
|
|
|
93
|
-
module.exports = ResultSet;
|
|
92
|
+
module.exports = ResultSet;
|
|
@@ -30,7 +30,6 @@ const Graph = require('../structure/graph').Graph;
|
|
|
30
30
|
* instance or a <code>RemoteConnection</code>.
|
|
31
31
|
*/
|
|
32
32
|
class AnonymousTraversalSource {
|
|
33
|
-
|
|
34
33
|
/**
|
|
35
34
|
* Creates a new instance of {@code AnonymousTraversalSource}.
|
|
36
35
|
* @param {Function} [traversalSourceClass] Optional {@code GraphTraversalSource} constructor.
|
|
@@ -75,4 +74,4 @@ class AnonymousTraversalSource {
|
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
module.exports = AnonymousTraversalSource;
|
|
77
|
+
module.exports = AnonymousTraversalSource;
|