This is an automated email from the ASF dual-hosted git repository. Cole-Greer pushed a commit to branch GValueFollowupTP4 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit cf356c9b66a1ba0d3af605abb97a797e342b46f9 Author: Cole Greer <[email protected]> AuthorDate: Tue Jun 9 15:36:52 2026 -0700 Add parameterize GValue feature-test variant to the JavaScript GLV Wires a PARAMETERIZE=true cucumber variant that wraps each non-g parameter in a GValue (matching the Python reference), and fixes the features-*-docker npm scripts to target the actual mounted corpus path (/gremlin-test) instead of the nonexistent ../gremlin-test, which had made the docker feature run pass vacuously. Also fixes GremlinLang._argAsString to merge a child/anonymous traversal's parameters into the parent so GValue bindings nested inside child traversals are sent to the server, resolving 'No variable found for <name>' errors. Adds unit coverage for the nested-binding merge. Assisted-by: Kiro:claude-opus-4.8 --- gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts | 9 ++++++++- gremlin-js/gremlin-javascript/package.json | 8 +++++--- .../gremlin-javascript/test/cucumber/feature-steps.js | 7 +++++++ .../gremlin-javascript/test/unit/gremlin-lang-test.js | 15 +++++++++++++++ 4 files changed, 35 insertions(+), 4 deletions(-) diff --git a/gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts b/gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts index 2c085a2c31..94542b2121 100644 --- a/gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts +++ b/gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts @@ -145,6 +145,9 @@ export default class GremlinLang { return this._argAsString(arg.id); } if (arg instanceof GremlinLang) { + // Merge the child's parameters so GValue bindings nested inside a child + // traversal are still sent to the server alongside the rendered query. + arg.parameters.forEach((v, k) => this.parameters.set(k, v)); return arg.getGremlin('__'); } if (typeof arg.getGremlinLang === 'function') { @@ -152,7 +155,11 @@ export default class GremlinLang { if (arg.graph != null) { throw new Error('Child traversal must be anonymous - use __ not g'); } - return arg.getGremlinLang().getGremlin('__'); + const childLang = arg.getGremlinLang(); + // Merge the child's parameters so GValue bindings nested inside a child + // traversal are still sent to the server alongside the rendered query. + childLang.parameters.forEach((v: any, k: string) => this.parameters.set(k, v)); + return childLang.getGremlin('__'); } if (arg instanceof Uint8Array) { return `Binary("${Buffer.from(arg.buffer, arg.byteOffset, arg.byteLength).toString('base64')}")`; diff --git a/gremlin-js/gremlin-javascript/package.json b/gremlin-js/gremlin-javascript/package.json index 5ec1746c92..6de05ae683 100644 --- a/gremlin-js/gremlin-javascript/package.json +++ b/gremlin-js/gremlin-javascript/package.json @@ -91,10 +91,12 @@ "integration-test": "npm run integration-test-graphbinary", "integration-test-graphbinary": "cross-env TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' mocha test/integration -t 5000", "TODO": "# test other mime types like graphbinary stringd", - "features": "npm run features-graphbinary", + "features": "npm run features-graphbinary && npm run features-graphbinary-params", "features-graphbinary": "cross-env NODE_OPTIONS='--loader ts-node/esm' TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' cucumber-js --tags \"not @DataBigDecimal and not @DataBigInt and not @DataUUID and not @DataLong and not @StepWrite\" --import test/cucumber ../../gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/", - "features-docker": "npm run features-graphbinary-docker", - "features-graphbinary-docker": "cross-env NODE_OPTIONS='--loader ts-node/esm' TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' cucumber-js --tags \"not @DataBigDecimal and not @DataBigInt and not @DataUUID and not @DataLong and not @StepWrite\" --import test/cucumber ../gremlin-test/", + "features-graphbinary-params": "cross-env NODE_OPTIONS='--loader ts-node/esm' TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' PARAMETERIZE=true cucumber-js --tags \"not @DataBigDecimal and not @DataBigInt and not @DataUUID and not @DataLong and not @StepWrite\" --import test/cucumber ../../gremlin-test/src/main/resources/org/apache/tinkerpop/gremlin/test/features/", + "features-docker": "npm run features-graphbinary-docker && npm run features-graphbinary-params-docker", + "features-graphbinary-docker": "cross-env NODE_OPTIONS='--loader ts-node/esm' TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' cucumber-js --tags \"not @DataBigDecimal and not @DataBigInt and not @DataUUID and not @DataLong and not @StepWrite\" --import test/cucumber /gremlin-test/", + "features-graphbinary-params-docker": "cross-env NODE_OPTIONS='--loader ts-node/esm' TS_NODE_PROJECT='tsconfig.test.json' CLIENT_MIMETYPE='application/vnd.graphbinary-v4.0' PARAMETERIZE=true cucumber-js --tags \"not @DataBigDecimal and not @DataBigInt and not @DataUUID and not @DataLong and not @StepWrite\" --import test/cucumber /gremlin-test/", "lint": "eslint --ext .js .", "doc": "typedoc --out doc --readme README.md --entryPointStrategy expand --entryPoints 'lib/**/*.ts' --tsconfig tsconfig.json --exclude 'lib/language/grammar/**'" }, diff --git a/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js b/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js index 08830a407e..4010dee697 100644 --- a/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js +++ b/gremlin-js/gremlin-javascript/test/cucumber/feature-steps.js @@ -33,8 +33,10 @@ import { Path, Vertex, Edge, Property } from '../../lib/structure/graph.js'; import { statics } from '../../lib/process/graph-traversal.js'; import { t, P, direction, merge, barrier, cardinality, column, order, TextP, IO, pick, pop, scope, operator, withOptions } from '../../lib/process/traversal.js'; import { toLong } from '../../lib/utils.js'; +import { GValue } from '../../lib/process/gvalue.js'; import anon from '../../lib/process/anonymous-traversal.js'; const __ = statics; +const parameterize = process.env.PARAMETERIZE === 'true'; import { deepMembersById } from './element-comparison.js'; import { Buffer } from 'buffer'; import GremlinLang from "../../lib/process/gremlin-lang.js"; @@ -141,6 +143,11 @@ Given('an unsupported test', () => {}); Given('the traversal of', function (traversalText) { const p = Object.assign({}, this.parameters); + if (parameterize) { + for (const k of Object.keys(p)) { + p[k] = new GValue(k, p[k]); + } + } p.g = this.g; this.traversal = gremlin[this.scenario].shift()(p); const sideEffectLang = new GremlinLang(); diff --git a/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js b/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js index abb86a1740..a983b64d03 100644 --- a/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js +++ b/gremlin-js/gremlin-javascript/test/unit/gremlin-lang-test.js @@ -711,6 +711,21 @@ describe('GremlinLang', function () { assert.deepStrictEqual(gl.getParameters().get('ids'), [1, 2, 3]); }); + it('should merge bindings from a GValue nested in a child traversal', function () { + const traversal = g.V().where(__.is(new GValue('xx1', 1))); + const gl = traversal.getGremlinLang(); + assert.strictEqual(gl.getGremlin(), 'g.V().where(__.is(xx1))'); + assert.deepStrictEqual(gl.getParameters().get('xx1'), 1); + }); + + it('should merge bindings from GValues nested across multiple child traversals', function () { + const traversal = g.union(__.V(new GValue('vid1', 1)), __.V(new GValue('vid4', 4))).values('name'); + const gl = traversal.getGremlinLang(); + assert.strictEqual(gl.getGremlin(), "g.union(__.V(vid1),__.V(vid4)).values('name')"); + assert.deepStrictEqual(gl.getParameters().get('vid1'), 1); + assert.deepStrictEqual(gl.getParameters().get('vid4'), 4); + }); + it('should reject non-string name', function () { assert.throws(() => new GValue(123, 'v'), /Invalid GValue name/); assert.throws(() => new GValue(null, 'v'), /Invalid GValue name/);
