Skip to content

Commit

Permalink
TINKERPOP-2421 Added requestOption capabilities for javascript
Browse files Browse the repository at this point in the history
This is a bit messy at the moment but all tests pass.
  • Loading branch information
spmallette committed Sep 21, 2020
1 parent 9150c64 commit 007c45c
Show file tree
Hide file tree
Showing 10 changed files with 129 additions and 116 deletions.
22 changes: 21 additions & 1 deletion gremlin-javascript/glv/GraphTraversalSource.template
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const { Traversal } = require('./traversal');
const remote = require('../driver/remote-connection');
const utils = require('../utils');
const Bytecode = require('./bytecode');
const { TraversalStrategies, VertexProgramStrategy } = require('./traversal-strategy');
const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy } = require('./traversal-strategy');


/**
Expand Down Expand Up @@ -76,6 +76,26 @@ class GraphTraversalSource {
configuration: configuration}));
}

/**
* Graph Traversal Source with method.
* @param {String} key
* @param {Object} value if not specified, the value with default to {@code true}
* @returns {GraphTraversalSource}
*/
with_(key, value=undefined) {
const val = value === undefined ? true : value;
let optionsStrategy = this.bytecode.sourceInstructions.find(
i => i[0] === "withStrategies" && i[1] instanceof OptionsStrategy);
if (optionsStrategy === undefined) {
optionsStrategy = new OptionsStrategy({[key]: val});
return this.withStrategies(optionsStrategy);
} else {
optionsStrategy[1].configuration[key] = val;
return new this.graphTraversalSourceClass(this.graph, new TraversalStrategies(this.traversalStrategies),
this.bytecode, this.graphTraversalSourceClass, this.graphTraversalClass);
}
}

/**
* Returns the string representation of the GraphTraversalSource.
* @returns {string}
Expand Down
1 change: 1 addition & 0 deletions gremlin-javascript/glv/generate.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def binding = ["enums": CoreImports.getClassImports()
!it.name.equals("clone") &&
// Use hardcoded name to be for forward-compatibility
!it.name.equals("withBindings") &&
!it.name.equals(TraversalSource.Symbols.with) &&
!it.name.equals(TraversalSource.Symbols.withRemote) &&
!it.name.equals(TraversalSource.Symbols.withComputer)
}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ class Client {
* Send a request to the Gremlin Server, can send a script or bytecode steps.
* @param {Bytecode|string} message The bytecode or script to send
* @param {Object} [bindings] The script bindings, if any.
* @param {Object} [requestOptions] Configuration specific to the current request.
* @returns {Promise}
*/
submit(message, bindings) {
submit(message, bindings, requestOptions=undefined) {
if (typeof message === 'string') {
const args = {
'gremlin': message,
Expand All @@ -83,12 +84,11 @@ class Client {
if (this._options.session && this._options.processor === 'session') {
args['session'] = this._options.session;
}

return this._connection.submit(null, 'eval', args, null, this._options.processor || '');
return this._connection.submit(null, 'eval', Object.assign(args, requestOptions), null, this._options.processor || '');
}

if (message instanceof Bytecode) {
return this._connection.submit(message);
return this._connection.submit(message, 'bytecode', requestOptions);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,19 @@ class Connection extends EventEmitter {

/** @override */
submit(bytecode, op, args, requestId, processor) {
const requestIdDefined = (args !== undefined && args !== null && 'requestId' in args)
const rid = requestId === undefined || requestId === null ? (requestIdDefined ? args['requestId'] : utils.getUuid()) : requestId;
if (requestIdDefined) delete args['requestId'];

return this.open().then(() => new Promise((resolve, reject) => {
if (requestId === null || requestId === undefined) {
requestId = utils.getUuid();
this._responseHandlers[requestId] = {
if (op !== 'authentication') {
this._responseHandlers[rid] = {
callback: (err, result) => err ? reject(err) : resolve(result),
result: null
};
}

const message = Buffer.from(this._header + JSON.stringify(this._getRequest(requestId, bytecode, op, args, processor)));
const message = Buffer.from(this._header + JSON.stringify(this._getRequest(rid, bytecode, op, args, processor)));
this._ws.send(message);
}));
}
Expand All @@ -185,17 +188,21 @@ class Connection extends EventEmitter {
_getRequest(id, bytecode, op, args, processor) {
if (args) {
args = this._adaptArgs(args, true);
} else {
args = {};
}

if (bytecode) {
args['gremlin'] = this._writer.adaptObject(bytecode);
args['aliases'] = { 'g': this.traversalSource }
}

return ({
'requestId': { '@type': 'g:UUID', '@value': id },
'op': op || 'bytecode',
// if using op eval need to ensure processor stays unset if caller didn't set it.
'processor': (!processor && op !== 'eval') ? 'traversal' : processor,
'args': args || {
'gremlin': this._writer.adaptObject(bytecode),
'aliases': { 'g': this.traversalSource }
}
'args': args
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const rcModule = require('./remote-connection');
const RemoteConnection = rcModule.RemoteConnection;
const RemoteTraversal = rcModule.RemoteTraversal;
const Client = require('./client');
const OptionsStrategy = require('../process/traversal-strategy').OptionsStrategy;

/**
* Represents the default {@link RemoteConnection} implementation.
Expand Down Expand Up @@ -60,7 +61,21 @@ class DriverRemoteConnection extends RemoteConnection {

/** @override */
submit(bytecode) {
return this._client.submit(bytecode).then(result => new RemoteTraversal(result.toArray()));
let optionsStrategy = bytecode.sourceInstructions.find(
i => i[0] === "withStrategies" && i[1] instanceof OptionsStrategy);
const allowedKeys = ['evaluationTimeout', 'scriptEvaluationTimeout', 'batchSize', 'requestId', 'userAgent'];

let requestOptions = undefined;
if (optionsStrategy !== undefined) {
requestOptions = {};
const conf = optionsStrategy[1].configuration;
for (let key in conf) {
if (conf.hasOwnProperty(key) && allowedKeys.indexOf(key) > -1) {
requestOptions[key] = conf[key];
}
}
}
return this._client.submit(bytecode, null, requestOptions).then(result => new RemoteTraversal(result.toArray()));
}

/** @override */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const { Traversal } = require('./traversal');
const remote = require('../driver/remote-connection');
const utils = require('../utils');
const Bytecode = require('./bytecode');
const { TraversalStrategies, VertexProgramStrategy } = require('./traversal-strategy');
const { TraversalStrategies, VertexProgramStrategy, OptionsStrategy } = require('./traversal-strategy');


/**
Expand Down Expand Up @@ -76,6 +76,26 @@ class GraphTraversalSource {
configuration: configuration}));
}

/**
* Graph Traversal Source with method.
* @param {String} key
* @param {Object} value if not specified, the value with default to {@code true}
* @returns {GraphTraversalSource}
*/
with_(key, value=undefined) {
const val = value === undefined ? true : value;
let optionsStrategy = this.bytecode.sourceInstructions.find(
i => i[0] === "withStrategies" && i[1] instanceof OptionsStrategy);
if (optionsStrategy === undefined) {
optionsStrategy = new OptionsStrategy({[key]: val});
return this.withStrategies(optionsStrategy);
} else {
optionsStrategy[1].configuration[key] = val;
return new this.graphTraversalSourceClass(this.graph, new TraversalStrategies(this.traversalStrategies),
this.bytecode, this.graphTraversalSourceClass, this.graphTraversalClass);
}
}

/**
* Returns the string representation of the GraphTraversalSource.
* @returns {string}
Expand All @@ -84,16 +104,6 @@ class GraphTraversalSource {
return 'graphtraversalsource[' + this.graph.toString() + ']';
}

/**
* Graph Traversal Source with method.
* @param {...Object} args
* @returns {GraphTraversalSource}
*/
with_(...args) {
const b = new Bytecode(this.bytecode).addSource('with', args);
return new this.graphTraversalSourceClass(this.graph, new TraversalStrategies(this.traversalStrategies), b, this.graphTraversalSourceClass, this.graphTraversalClass);
}

/**
* Graph Traversal Source withBulk method.
* @param {...Object} args
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
*/
'use strict';

const Traversal = require('./traversal').Traversal;

class TraversalStrategies {
/**
* Creates a new instance of TraversalStrategies.
Expand Down Expand Up @@ -60,9 +62,9 @@ class TraversalStrategy {

/**
* @param {String} fqcn fully qualified class name in Java of the strategy
* @param {Map} configuration for the strategy
* @param {Object} configuration for the strategy
*/
constructor(fqcn, configuration = new Map()) {
constructor(fqcn, configuration = {}) {
this.fqcn = fqcn;
this.configuration = configuration;
}
Expand Down Expand Up @@ -97,12 +99,12 @@ class HaltedTraverserStrategy extends TraversalStrategy {
constructor(haltedTraverserFactory) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy");
if (haltedTraverserFactory !== undefined)
this.configuration.set("haltedTraverserFactory", haltedTraverserFactory);
this.configuration["haltedTraverserFactory"] = haltedTraverserFactory;
}
}

class OptionsStrategy extends TraversalStrategy {
constructor(options = new Map()) {
constructor(options) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy", options);
}
}
Expand All @@ -116,15 +118,7 @@ class PartitionStrategy extends TraversalStrategy {
* @param {boolean} [options.includeMetaProperties] determines if meta-properties should be included in partitioning defaulting to false
*/
constructor(options) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy");
if (options.partitionKey !== undefined)
this.configuration.set("partitionKey", options.partitionKey);
if (options.writePartition !== undefined)
this.configuration.set("writePartition", options.writePartition);
if (options.readPartitions !== undefined)
this.configuration.set("readPartitions", options.readPartitions);
if (options.includeMetaProperties !== undefined)
this.configuration.set("includeMetaProperties", options.includeMetaProperties);
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy", options);
}
}

Expand All @@ -137,39 +131,20 @@ class SubgraphStrategy extends TraversalStrategy {
* @param {boolean} [options.checkAdjacentVertices] enables the strategy to apply the {@code vertices} filter to the adjacent vertices of an edge.
*/
constructor(options) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy");
if (options.vertices !== undefined)
this.configuration.set("vertices", options.vertices);
if (options.edges !== undefined)
this.configuration.set("edges", options.edges);
if (options.vertexProperties !== undefined)
this.configuration.set("vertexProperties", options.vertexProperties);
if (options.checkAdjacentVertices !== undefined)
this.configuration.set("checkAdjacentVertices", options.checkAdjacentVertices);
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy", options);
if (this.configuration.vertices instanceof Traversal)
this.configuration.vertices = this.configuration.vertices.bytecode;
if (this.configuration.edges instanceof Traversal)
this.configuration.edges = this.configuration.edges.bytecode;
if (this.configuration.vertexProperties instanceof Traversal)
this.configuration.vertexProperties = this.configuration.vertexProperties.bytecode;
}
}

class VertexProgramStrategy extends TraversalStrategy {

constructor(options) {
super("org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy");
this.configuration = new Map();
if (options.graphComputer !== undefined)
this.configuration.set("graphComputer", options.graphComputer);
if (options.workers !== undefined)
this.configuration.set("workers", options.workers);
if (options.persist !== undefined)
this.configuration.set("persist", options.persist);
if (options.result !== undefined)
this.configuration.set("result", options.result);
if (options.vertices !== undefined)
this.configuration.set("vertices", options.vertices);
if (options.edges !== undefined)
this.configuration.set("edges", options.edges);
if (options.configuration !== undefined)
options.configuration.forEach(function(k,v) {
this.configuration.set(k, v);
});
super("org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy", options);
}
}

Expand All @@ -179,8 +154,8 @@ class MatchAlgorithmStrategy extends TraversalStrategy {
*/
constructor(matchAlgorithm) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.MatchAlgorithmStrategy");
if (graphComputer !== undefined)
this.configuration.set("matchAlgorithm", matchAlgorithm);
if (matchAlgorithm !== undefined)
this.configuration["matchAlgorithm"] = matchAlgorithm;
}
}

Expand Down Expand Up @@ -286,9 +261,8 @@ class EdgeLabelVerificationStrategy extends TraversalStrategy {
* @param {boolean} throwException determines if exceptions should be thrown when verifications fails
*/
constructor(logWarnings = false, throwException=false) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
this.configuration.set("logWarnings", logWarnings);
this.configuration.set("throwException", throwException);
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy",
{logWarnings: logWarnings, throwException: throwException});
}
}

Expand All @@ -299,10 +273,8 @@ class ReservedKeysVerificationStrategy extends TraversalStrategy {
* @param {Array<String>} keys the list of reserved keys to verify
*/
constructor(logWarnings = false, throwException=false, keys=["id", "label"]) {
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy");
this.configuration.set("logWarnings", logWarnings);
this.configuration.set("throwException", throwException);
this.configuration.set("keys", keys);
super("org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy",
{logWarnings: logWarnings, throwException: throwException, keys: keys});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,9 +256,16 @@ class TraverserSerializer extends TypeSerializer {
class TraversalStrategySerializer extends TypeSerializer {
/** @param {TraversalStrategy} item */
serialize(item) {
const conf = {};
for (let k in item.configuration) {
if (item.configuration.hasOwnProperty(k)) {
conf[k] = this.writer.adaptObject(item.configuration[k]);
}
}

return {
[typeKey]: 'g:' + item.constructor.name,
[valueKey]: item.configuration
[valueKey]: conf
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,19 @@ describe('Traversal', function () {
g.V().count().next().then(function (item1) {
assert.ok(item1);
assert.strictEqual(item1.value, 4);
});
}, (err) => assert.fail("tanked: " + err));
g.E().count().next().then(function (item1) {
assert.ok(item1);
assert.strictEqual(item1.value, 0);
});
}, (err) => assert.fail("tanked: " + err));
g.V().label().dedup().count().next().then(function (item1) {
assert.ok(item1);
assert.strictEqual(item1.value, 1);
});
}, (err) => assert.fail("tanked: " + err));
g.V().label().dedup().next().then(function (item1) {
assert.ok(item1);
assert.strictEqual(item1.value, "person");
});
}, (err) => assert.fail("tanked: " + err));
});
it('should allow ReadOnlyStrategy', function() {
const g = traversal().withRemote(connection).withStrategies(new ReadOnlyStrategy());
Expand All @@ -181,7 +181,11 @@ describe('Traversal', function () {
assert.ok(item1);
assert.strictEqual(item1.value, 6);
});
return g.V().out().iterate().then(() => assert.fail("should have tanked"), (err) => assert.ok(err));
return g.V().out().iterate().then(() => assert.fail("should have tanked"), (err) => assert.strictEqual(err.statusCode, 500));
});
it('should allow with_(evaluationTimeout,10)', function() {
const g = traversal().withRemote(connection).with_('x').with_('evaluationTimeout', 10);
return g.V().repeat(__.both()).iterate().then(() => assert.fail("should have tanked"), (err) => assert.strictEqual(err.statusCode, 598));
});
});
});
Loading

0 comments on commit 007c45c

Please sign in to comment.