This is an automated email from the ASF dual-hosted git repository.

jorgebg pushed a commit to branch TINKERPOP-2070
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/TINKERPOP-2070 by this push:
     new 34a869f  Introduce ResultSet abstraction
34a869f is described below

commit 34a869fa8553e15bded0f01453bf4e2904388a88
Author: Jorge Bay Gondra <jorgebaygon...@gmail.com>
AuthorDate: Tue Oct 23 12:13:11 2018 +0200

    Introduce ResultSet abstraction
    
    - Client and Connection submit() method returns a ResultSet instance
    - DriverRemoteConnection returns an instance of RemoteTraversal
    - Include gremlin script submission via gremlin-javascript in
    reference docs
    - ResultSet tests and other test fixes
---
 docs/src/reference/gremlin-variants.asciidoc       | 23 ++++++
 docs/src/upgrade/release-3.2.x-incubating.asciidoc | 29 +++----
 .../main/javascript/gremlin-javascript/index.js    | 14 ++--
 .../gremlin-javascript/lib/driver/client.js        |  2 +-
 .../gremlin-javascript/lib/driver/connection.js    |  5 +-
 .../lib/driver/driver-remote-connection.js         |  6 +-
 .../lib/driver/remote-connection.js                |  3 +
 .../gremlin-javascript/lib/driver/result-set.js    | 92 ++++++++++++++++++++++
 .../javascript/gremlin-javascript/lib/utils.js     |  6 +-
 .../javascript/gremlin-javascript/test/helper.js   | 15 ++--
 .../test/integration/client-tests.js               | 31 ++++----
 .../gremlin-javascript/test/unit/exports-test.js   | 18 +++--
 .../test/unit/result-set-test.js                   | 86 ++++++++++++++++++++
 13 files changed, 274 insertions(+), 56 deletions(-)

diff --git a/docs/src/reference/gremlin-variants.asciidoc 
b/docs/src/reference/gremlin-variants.asciidoc
index 074c436..3852591 100644
--- a/docs/src/reference/gremlin-variants.asciidoc
+++ b/docs/src/reference/gremlin-variants.asciidoc
@@ -562,3 +562,26 @@ const __ = gremlin.process.statics;
 
 g.V().repeat(__.out()).times(2).values("name").fold().toList();
 ----
+
+=== Submit Gremlin Scripts
+
+Additionally, you can also send parametrized Gremlin scripts to the server as 
strings, using the
+`Client` class in Gremlin-JavaScript.
+
+
+[source,javascript]
+----
+const gremlin = require('gremlin');
+const client = new gremlin.driver.Client('ws://localhost:8182/gremlin', { 
traversalSource: 'g' });
+
+const result1 = await client.submit('g.V(vid)', { vid: 1 });
+const vertex = result1.first();
+
+const result2 = await client.submit('g.V().hasLabel(label).tail(n)', { label: 
'person', n: 3 });
+
+// ResultSet is an iterable
+for (const vertex of result2) {
+  console.log(vertex.id);
+}
+
+----
\ No newline at end of file
diff --git a/docs/src/upgrade/release-3.2.x-incubating.asciidoc 
b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
index 49e222a..9fc3002 100644
--- a/docs/src/upgrade/release-3.2.x-incubating.asciidoc
+++ b/docs/src/upgrade/release-3.2.x-incubating.asciidoc
@@ -112,18 +112,19 @@ Gremlin Javascript can now submit script, with optional 
bindings, using the `Cli
 [source,javascript]
 ----
 const gremlin = require('gremlin');
-const connection = new gremlin.driver.Client('ws://localhost:8182/gremlin', { 
traversalSource: 'g' });
+const client = new gremlin.driver.Client('ws://localhost:8182/gremlin', { 
traversalSource: 'g' });
 
-connection.submit('g.V().tail()')
-    .then(response => {
-        console.log(response.traversers.length);
-        console.log(response.traversers[0]);
+client.submit('g.V().tail()')
+    .then(result => {
+        console.log(result.length);
+        console.log(result.toArray()[0]);
     });
 
-connection.submit('g.V(vid)', {vid: 1})
-    .then(response => {
-        console.log(response.traversers.length);
-        console.log(response.traversers[0]);
+client.submit('g.V(vid)', { vid: 1 })
+    .then(result => {
+        console.log(result.length);
+        // Get the first item
+        console.log(result.first());
     });
 ----
 
@@ -133,16 +134,16 @@ and also allows translation of bytecode steps into script:
 ----
 const gremlin = require('gremlin');
 const graph = new gremlin.process.Graph();
-const connection = new gremlin.driver.Client('ws://localhost:8182/gremlin', { 
traversalSource: 'g' });
+const client = new gremlin.driver.Client('ws://localhost:8182/gremlin', { 
traversalSource: 'g' });
 const translator = new gremlin.process.Translator('g');
 
 const g = graph.traversal();
 const script = translator.translate(g.V().tail().getBytecode());
 
-connection.submit(script)
-    .then(response => {
-        console.log(response.traversers.length);
-        console.log(response.traversers[0]);
+client.submit(script)
+    .then(result => {
+        console.log(result.length);
+        console.log(result.first());
     });
 ----
 
diff --git a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
index ffc7f0c..7f8c80f 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/index.js
@@ -33,6 +33,7 @@ const Translator = require('./lib/process/translator');
 const utils = require('./lib/utils');
 const DriverRemoteConnection = 
require('./lib/driver/driver-remote-connection');
 const Client = require('./lib/driver/client');
+const ResultSet = require('./lib/driver/result-set');
 const Authenticator = require('./lib/driver/auth/authenticator');
 const PlainTextSaslAuthenticator = 
require('./lib/driver/auth/plain-text-sasl-authenticator');
 
@@ -41,15 +42,16 @@ module.exports = {
     RemoteConnection: rc.RemoteConnection,
     RemoteStrategy: rc.RemoteStrategy,
     RemoteTraversal: rc.RemoteTraversal,
-    DriverRemoteConnection: DriverRemoteConnection,
-    Client: Client,
+    DriverRemoteConnection,
+    Client,
+    ResultSet,
     auth: {
-      Authenticator: Authenticator,
-      PlainTextSaslAuthenticator: PlainTextSaslAuthenticator
+      Authenticator,
+      PlainTextSaslAuthenticator
     }
   },
   process: {
-    Bytecode: Bytecode,
+    Bytecode,
     EnumValue: t.EnumValue,
     P: t.P,
     Traversal: t.Traversal,
@@ -70,7 +72,7 @@ module.exports = {
     GraphTraversal: gt.GraphTraversal,
     GraphTraversalSource: gt.GraphTraversalSource,
     statics: gt.statics,
-    Translator: Translator
+    Translator
   },
   structure: {
     io: {
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/client.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/client.js
index ccb7b8b..79fa91e 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/client.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/client.js
@@ -61,7 +61,7 @@ class Client {
    * @returns {Promise}
    */
   submit(message, bindings) {
-    if (typeof message === 'string' || message instanceof String) {
+    if (typeof message === 'string') {
       const args = {
         'gremlin': message,
         'bindings': bindings, 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js
index 0f1e732..cc03449 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/connection.js
@@ -26,6 +26,7 @@ const WebSocket = require('ws');
 const util = require('util');
 const utils = require('../utils');
 const serializer = require('../structure/io/graph-serializer');
+const ResultSet = require('./result-set');
 
 const responseStatusCode = {
   success: 200,
@@ -192,7 +193,7 @@ class Connection {
     switch (response.status.code) {
       case responseStatusCode.noContent:
         this._clearHandler(response.requestId);
-        return handler.callback(null, { traversers: []});
+        return handler.callback(null, new ResultSet(utils.emptyArray));
       case responseStatusCode.partialContent:
         handler.result = handler.result || [];
         handler.result.push.apply(handler.result, response.result.data);
@@ -205,7 +206,7 @@ class Connection {
           handler.result = response.result.data;
         }
         this._clearHandler(response.requestId);
-        return handler.callback(null, { traversers: handler.result });
+        return handler.callback(null, new ResultSet(handler.result));
     }
   }
 
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js
index f4021d4..2ed7ba5 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/driver-remote-connection.js
@@ -22,7 +22,9 @@
  */
 'use strict';
 
-const RemoteConnection = require('./remote-connection').RemoteConnection;
+const rcModule = require('./remote-connection');
+const RemoteConnection = rcModule.RemoteConnection;
+const RemoteTraversal = rcModule.RemoteTraversal;
 const Client = require('./client');
 
 /**
@@ -58,7 +60,7 @@ class DriverRemoteConnection extends RemoteConnection {
 
   /** @override */
   submit(bytecode) {
-    return this._client.submit(bytecode);
+    return this._client.submit(bytecode).then(result => new 
RemoteTraversal(result.toArray()));
   }
 
   /** @override */
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/remote-connection.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/remote-connection.js
index 9f70679..cc4c4d2 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/remote-connection.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/remote-connection.js
@@ -61,6 +61,9 @@ class RemoteConnection {
   }
 }
 
+/**
+ * Represents a traversal as a result of a {@link RemoteConnection} submission.
+ */
 class RemoteTraversal extends t.Traversal {
   constructor(traversers, sideEffects) {
     super(null, null, null);
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/result-set.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/result-set.js
new file mode 100644
index 0000000..1860aea
--- /dev/null
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/driver/result-set.js
@@ -0,0 +1,92 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+/**
+ * @author Jorge Bay Gondra
+ */
+'use strict';
+
+const util = require('util');
+const inspect = util.inspect.custom || 'inspect';
+
+/**
+ * Represents the response returned from the execution of a Gremlin traversal 
or script.
+ */
+class ResultSet {
+
+  /**
+   * Creates a new instance of {@link ResultSet}.
+   * @param {Array} items
+   */
+  constructor(items) {
+    if (!Array.isArray(items)) {
+      throw new TypeError('items must be an Array instance');
+    }
+
+    this._items = items;
+
+    /**
+     * Gets the amount of items in the result.
+     * @type {Number}
+     */
+    this.length = items.length;
+
+    /**
+     * Access the raw result items via a property.
+     * @deprecated It will be removed in Apache TinkerPop version 3.4.
+     * Use <code>toArray()</code> or iterate directly from the 
<code>ResultSet</code>.
+     * @type {Array}
+     */
+    this.traversers = items;
+  }
+
+  /**
+   * Gets the iterator associated with this instance.
+   * @returns {Iterator}
+   */
+  [Symbol.iterator]() {
+    return this._items[Symbol.iterator]();
+  }
+
+  /**
+   * Provides a representation useful for debug and tracing.
+   */
+  [inspect]() {
+    return this._items;
+  }
+
+  /**
+   * Gets an array of result items.
+   * @returns {Array}
+   */
+  toArray() {
+    return this._items;
+  }
+
+  /**
+   * Returns the first item.
+   * @returns {Object|null}
+   */
+  first() {
+    const item = this._items[0];
+    return item !== undefined ? item : null;
+  }
+}
+
+module.exports = ResultSet;
\ No newline at end of file
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js
index 7abc5fe..c6b091f 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/lib/utils.js
@@ -31,7 +31,7 @@ exports.toLong = function toLong(value) {
 
 const Long = exports.Long = function Long(value) {
   if (typeof value !== 'string' && typeof value !== 'number') {
-    throw new TypeError('Ty')
+    throw new TypeError('The value must be a string or a number');
   }
   this.value = value.toString();
 };
@@ -53,4 +53,6 @@ exports.getUuid = function getUuid() {
     hex.substr(12, 4) + '-' +
     hex.substr(16, 4) + '-' +
     hex.substr(20, 12));
-}
\ No newline at end of file
+};
+
+exports.emptyArray = Object.freeze([]);
\ No newline at end of file
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/helper.js 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/helper.js
index 53358c3..f819ca3 100644
--- a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/helper.js
+++ b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/helper.js
@@ -26,20 +26,23 @@ const DriverRemoteConnection = 
require('../lib/driver/driver-remote-connection')
 const Client = require('../lib/driver/client');
 const PlainTextSaslAuthenticator = 
require('../lib/driver/auth/plain-text-sasl-authenticator');
 
+const serverUrl = 'ws://localhost:45940/gremlin';
+const serverAuthUrl = 'ws://localhost:45941/gremlin';
+
 /** @returns {DriverRemoteConnection} */
 exports.getConnection = function getConnection(traversalSource) {
-  return new DriverRemoteConnection('ws://localhost:45940/gremlin', { 
traversalSource: traversalSource });
+  return new DriverRemoteConnection(serverUrl, { traversalSource });
 };
 
 exports.getSecureConnectionWithPlainTextSaslAuthenticator = (traversalSource, 
username, password) => {
   const authenticator = new PlainTextSaslAuthenticator(username, password);
-  return new DriverRemoteConnection('ws://localhost:45941/gremlin', { 
-    traversalSource: traversalSource, 
-    authenticator: authenticator, 
-    rejectUnauthorized: false 
+  return new DriverRemoteConnection(serverAuthUrl, {
+    traversalSource,
+    authenticator,
+    rejectUnauthorized: false
   });
 };
 
 exports.getClient = function getClient(traversalSource) {
-  return new Client('ws://localhost:45940/gremlin', { traversalSource: 
traversalSource });
+  return new Client(serverUrl, { traversalSource });
 };
\ No newline at end of file
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
index ffad6d6..92e709c 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/integration/client-tests.js
@@ -38,35 +38,32 @@ describe('Client', function () {
   describe('#submit()', function () {
     it('should send bytecode', function () {
       return client.submit(new Bytecode().addStep('V', []).addStep('tail', []))
-        .then(function (response) {
-          assert.ok(response);
-          assert.ok(response.traversers);
-          assert.strictEqual(response.traversers.length, 1);
-          assert.ok(response.traversers[0].object instanceof 
graphModule.Vertex);
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          assert.ok(result.first().object instanceof graphModule.Vertex);
         });
     });
     it('should send and parse a script', function () {
       return client.submit('g.V().tail()')
-        .then(function (response) {
-          assert.ok(response);
-          assert.ok(response.traversers);
-          assert.strictEqual(response.traversers.length, 1);
-          assert.ok(response.traversers[0] instanceof graphModule.Vertex);
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.length, 1);
+          assert.ok(result.first() instanceof graphModule.Vertex);
         });
     });
     it('should send and parse a script with bindings', function () {
       return client.submit('x + x', { x: 3 })
-        .then(function (response) {
-          assert.ok(response);
-          assert.ok(response.traversers);
-          assert.strictEqual(response.traversers[0], 6);
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.first(), 6);
         });
     });
     it('should send and parse a script with non-native javascript bindings', 
function () {
       return client.submit('card.class.simpleName + ":" + card', { card: 
t.cardinality.set } )
-        .then(function (response) {
-          assert.ok(response);
-          assert.strictEqual(response.traversers[0], 'Cardinality:set');
+        .then(function (result) {
+          assert.ok(result);
+          assert.strictEqual(result.first(), 'Cardinality:set');
         });
     });
   });
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/exports-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/exports-test.js
index e0dfb12..b12bfed 100644
--- 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/exports-test.js
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/exports-test.js
@@ -66,10 +66,16 @@ describe('API', function () {
   });
   it('should expose fields under driver', function () {
     assert.ok(glvModule.driver);
-    assert.strictEqual(typeof glvModule.driver.RemoteConnection, 'function');
-    assert.strictEqual(typeof glvModule.driver.RemoteStrategy, 'function');
-    assert.strictEqual(typeof glvModule.driver.RemoteTraversal, 'function');
-    assert.strictEqual(typeof glvModule.driver.DriverRemoteConnection, 
'function');
-    assert.strictEqual(glvModule.driver.DriverRemoteConnection.name, 
'DriverRemoteConnection');
+    validateConstructor(glvModule.driver, 'RemoteConnection');
+    validateConstructor(glvModule.driver, 'RemoteStrategy');
+    validateConstructor(glvModule.driver, 'RemoteTraversal');
+    validateConstructor(glvModule.driver, 'DriverRemoteConnection');
+    validateConstructor(glvModule.driver, 'Client');
+    validateConstructor(glvModule.driver, 'ResultSet');
   });
-});
\ No newline at end of file
+});
+
+function validateConstructor(parent, name) {
+  assert.strictEqual(typeof parent[name], 'function');
+  assert.strictEqual(parent[name].name, name);
+}
\ No newline at end of file
diff --git 
a/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/result-set-test.js
 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/result-set-test.js
new file mode 100644
index 0000000..9eb99af
--- /dev/null
+++ 
b/gremlin-javascript/src/main/javascript/gremlin-javascript/test/unit/result-set-test.js
@@ -0,0 +1,86 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+/**
+ * @author Jorge Bay Gondra
+ */
+'use strict';
+
+const assert = require('assert');
+const util = require('util');
+const ResultSet = require('../../lib/driver/result-set');
+
+describe('ResultSet', function () {
+
+  describe('#toArray()', () => {
+    it('should return an array of items', () => {
+      const items = [ 'a', 'b' ];
+      const result = new ResultSet(items);
+      assert.ok(Array.isArray(result.toArray()));
+      assert.deepStrictEqual(result.toArray(), items);
+    });
+  });
+
+  describe('#length', () => {
+    it('should return the length of the items', () => {
+      const items = [ 'a', 'b', 1, 0 ];
+      const result = new ResultSet(items);
+      assert.strictEqual(result.length, items.length);
+    });
+  });
+
+  describe('#first()', () => {
+    it('should return the first item when there are one or more than one 
item', () => {
+      assert.strictEqual(new ResultSet(['a', 'b']).first(), 'a');
+      assert.strictEqual(new ResultSet(['z']).first(), 'z');
+    });
+
+    it('should return null when there are no items', () => {
+      assert.strictEqual(new ResultSet([]).first(), null);
+    });
+  });
+
+  describe('#[Symbol.iterator]()', () => {
+    it('should support be iterable', () => {
+      const items = [ 1, 2, 3 ];
+      const result = new ResultSet(items);
+      const obtained = [];
+      for (let item of result) {
+        obtained.push(item);
+      }
+
+      assert.deepStrictEqual(obtained, items);
+      assert.deepStrictEqual(Array.from(result), items);
+    });
+  });
+
+  describe('#[util.inspect.custom]()', () => {
+    it('should return the Array representation', () => {
+      assert.strictEqual(util.inspect(new ResultSet([ 1, 2, 3 ])), '[ 1, 2, 3 
]');
+    });
+  });
+
+  describe('#traversers', () => {
+    it('should expose deprecated property', () => {
+      const items = [ 'a', 'b' ];
+      // Traversers property is going to be removed in upcoming versions
+      assert.strictEqual(new ResultSet(items).traversers, items);
+    });
+  });
+});
\ No newline at end of file

Reply via email to