Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima
** JavaScript: adopted `undici` for the default dispatcher; renamed `reader`->`responseSerializer`; added `readTimeoutMillis`, `keepAliveTimeMillis`, `maxResponseHeaderBytes`, `proxy`, `compression`, `batchSize`, `bulkResults`, `logger`; removed `headers` (use interceptors) and `ca`/`cert`/`pfx`/`rejectUnauthorized`/`agent` (TLS via the Node/undici runtime); undici is swapped out in browser bundles. *(breaking)*
** All drivers: compression now defaults on; `connectTimeout` lowered to 5s and applied to transport establishment; `readTimeout` is a streaming-safe idle-read timeout (off by default); `idleTimeout` reaps only pooled connections.
* Changed all GLV drivers to always serialize requests as JSON (`application/json`). Request serialization is no longer configurable and the serializer options (now `responseSerializer`) control only response deserialization.
* Fixed remote traversals in Java, Python, .NET, and JavaScript to avoid sending the traversal source alias as a GremlinLang parameter, and removed the unused GremlinLang traversal-source helpers.
* Fixed `gremlin-javascript` `Client.submit()` so that an explicit `bulkResults: false` request option is forwarded to the server instead of being silently dropped.
* Fixed `gremlin-dotnet` deflate response decompression, which threw on the server's zlib-framed output because it used `DeflateStream` (raw DEFLATE, RFC 1951) instead of `ZLibStream` (zlib, RFC 1950); the bug was previously masked because compression was off by default.
* Fixed `gremlin-dotnet` SSL options cloning (used on the skip-certificate-validation path) to copy `ClientCertificateContext` and `AllowTlsResume`, which were previously dropped, breaking mTLS client certificates and silently re-enabling TLS resumption.
Expand Down
27 changes: 27 additions & 0 deletions docs/src/upgrade/release-4.x.x.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,33 @@ RequestMessage.build("g.V(x)").addParameters(params).create();

See: link:https://issues.apache.org/jira/browse/TINKERPOP-3262[TINKERPOP-3262]

==== Traversal Source Not In Parameters

The traversal source alias for an HTTP request is selected with the top-level `g` request field. Query `parameters`
are only the named values referenced by the Gremlin string. Earlier 3.8.x driver builds could place the traversal
source alias into `bindings` as `g` (reminder: `bindings` have been renamed to `parameters`). Gremlin Server will ignore
this "g" value in "parameters" so use the `g` field instead.

Applications that construct HTTP requests directly should keep the traversal source alias out of `parameters` and set
the top-level `g` field instead:

[source,text]
----
// old
{
"gremlin": "g.V(x)",
"parameters": "[\"g\":\"gmodern\",\"x\":1]"
}

// new
{
"gremlin": "g.V(x)",
"g": "gmodern",
"parameters": "[\"x\":1]"
}
----


==== Standardizing GLV Connection Options

TinkerPop 4.x standardizes connection option names and defaults across all five Gremlin Language Variants (Java, Python,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,13 +495,6 @@ public static String convertParametersToString(final Map<String, Object> params)
return sb.toString();
}

/**
* The alias to set.
*/
public void addG(final String g) {
parameters.put("g", g);
}

/**
* Add a {@link TraversalSource} instruction to the GremlinLang.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ public async Task<ITraversal<TStart, TEnd>> SubmitAsync<TStart, TEnd>(GremlinLan
CancellationToken cancellationToken = default)
{
_logger.SubmittingGremlinLang(gremlinLang);
gremlinLang.AddG(_traversalSource);

var requestMsg = RequestMessage.Build(gremlinLang.GetGremlin())
.AddG(_traversalSource);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,6 @@ public async Task<ITraversal<TStart, TEnd>> SubmitAsync<TStart, TEnd>(GremlinLan
throw new InvalidOperationException("Transaction is not open");
}

gremlinLang.AddG(_traversalSource);

var requestMsg = RequestMessage.Build(gremlinLang.GetGremlin())
.AddG(_traversalSource);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,6 @@ public void AddStep(string stepName, params object?[] arguments)
AddToGremlin(stepName, arguments);
}

/// <summary>
/// Sets the alias for the traversal source.
/// </summary>
/// <param name="g">The alias to set.</param>
public void AddG(string g)
{
_parameters["g"] = g;
}

/// <summary>
/// Appends raw text to the gremlin string. Used for temporary options rendering.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,26 @@ public async Task ShouldBuildRequestWithParameters()
Assert.NotNull(capturedRequest);
var parametersString = (string)capturedRequest!.Fields[Tokens.ArgsParameters];
Assert.Contains("\"x\":42", parametersString);
Assert.DoesNotContain("\"g\":", parametersString);
}

[Fact]
public async Task ShouldNotBuildRequestWithTraversalSourceAsParameter()
{
RequestMessage? capturedRequest = null;
var client = CreateCapturingClient(msg => capturedRequest = msg);
var connection = new DriverRemoteConnection(client, "mySource");

var gl = new GremlinLang();
gl.AddStep("V", Array.Empty<object>());

await connection.SubmitAsync<object, object>(gl);

Assert.NotNull(capturedRequest);
var parametersString = capturedRequest!.Fields.TryGetValue(Tokens.ArgsParameters, out var parameters)
? (string) parameters
: "";
Assert.DoesNotContain("\"g\":", parametersString);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ public <E> CompletableFuture<RemoteTraversal<?, E>> submitAsync(final GremlinLan
}

try {
gremlinLang.addG(remoteTraversalSourceName);
return client.submitAsync(gremlinLang.getGremlin(), getRequestOptions(gremlinLang))
.thenApply(rs -> new DriverRemoteTraversal<>(rs, client, attachElements, conf));
} catch (Exception ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,30 @@
*/
package org.apache.tinkerpop.gremlin.driver.remote;

import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.RequestOptions;
import org.apache.tinkerpop.gremlin.util.Tokens;
import org.apache.tinkerpop.gremlin.driver.ResultSet;
import org.apache.tinkerpop.gremlin.process.traversal.GremlinLang;
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.step.GValue;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.apache.tinkerpop.gremlin.util.Tokens;
import org.junit.Test;
import org.mockito.ArgumentCaptor;

import java.util.Collections;
import java.util.concurrent.CompletableFuture;

import static org.apache.tinkerpop.gremlin.driver.RequestOptions.getRequestOptions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
* @author Stephen Mallette (http://stephen.genoprime.com)
Expand Down Expand Up @@ -55,4 +71,45 @@ public void shouldBuildRequestOptionsWithNumerics() {
assertEquals(Integer.valueOf(100), options.getBatchSize().get());
assertEquals(Long.valueOf(1000), options.getTimeoutMillis().get());
}

@Test
public void shouldSubmitTraversalWithoutTraversalSourceAsParameter() throws Exception {
final Client client = mockClient();
final DriverRemoteConnection connection = DriverRemoteConnection.using(client, "mySource");

connection.submitAsync(g.V().asAdmin().getGremlinLang()).get();

final RequestOptions options = captureRequestOptions(client, "g.V()");
assertFalse(options.getParameters().orElse("").contains("\"g\":"));
}

@Test
public void shouldSubmitTraversalWithOnlyExplicitParameters() throws Exception {
final Client client = mockClient();
final DriverRemoteConnection connection = DriverRemoteConnection.using(client, "mySource");
final GremlinLang gremlinLang = g.V(GValue.of("x", 42)).asAdmin().getGremlinLang();

connection.submitAsync(gremlinLang).get();

final RequestOptions options = captureRequestOptions(client, "g.V(x)");
assertTrue(options.getParameters().isPresent());
assertTrue(options.getParameters().get().contains("\"x\":42"));
assertFalse(options.getParameters().get().contains("\"g\":"));
}

private static Client mockClient() {
final Client client = mock(Client.class);
final ResultSet resultSet = mock(ResultSet.class);
when(resultSet.iterator()).thenReturn(Collections.emptyIterator());
when(client.alias("mySource")).thenReturn(client);
when(client.submitAsync(anyString(), any(RequestOptions.class)))
.thenReturn(CompletableFuture.completedFuture(resultSet));
return client;
}

private static RequestOptions captureRequestOptions(final Client client, final String gremlin) {
final ArgumentCaptor<RequestOptions> optionsCaptor = ArgumentCaptor.forClass(RequestOptions.class);
verify(client).submitAsync(eq(gremlin), optionsCaptor.capture());
return optionsCaptor.getValue();
}
}
4 changes: 0 additions & 4 deletions gremlin-go/driver/gremlinlang.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,6 @@ func ConvertParametersToString(params map[string]interface{}) string {
return sb.String()
}

func (gl *GremlinLang) AddG(g string) {
gl.parameters["g"] = g
}

func (gl *GremlinLang) AddSource(name string, arguments ...interface{}) {
if (name == "withStrategies" || name == "withoutStrategies") && len(arguments) != 0 {
args := gl.buildStrategyArgs(arguments...)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,6 @@ export default class DriverRemoteConnection extends RemoteConnection {
}

#buildRequestArgs(gremlinLang: GremlinLang) {
gremlinLang.addG(this.options.traversalSource || 'g');

let requestOptions: RequestOptions | undefined = undefined;
const strategies = gremlinLang.getOptionsStrategies();
if (strategies.length > 0) {
Expand Down
6 changes: 1 addition & 5 deletions gremlin-js/gremlin-javascript/lib/process/gremlin-lang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,6 @@ export default class GremlinLang {
this.gremlin += text;
}

addG(g: string): void {
this.parameters.set('g', g);
}

getParameters(): Map<string, any> {
return this.parameters;
}
Expand Down Expand Up @@ -304,4 +300,4 @@ export default class GremlinLang {
});
return '[' + parts.join(',') + ']';
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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.
*/

import assert from 'assert';
import DriverRemoteConnection from '../../lib/driver/driver-remote-connection.js';
import GremlinLang from '../../lib/process/gremlin-lang.js';

describe('DriverRemoteConnection', function () {
function createConnection(capture) {
const connection = new DriverRemoteConnection('http://localhost:8182/gremlin',
{ traversalSource: 'mySource', connectOnStartup: false });
connection._client = {
stream(gremlin, parameters, requestOptions) {
capture({ gremlin, parameters, requestOptions });
return (async function* () {})();
},
};
return connection;
}

it('does not send traversal source as a parameter', async function () {
let captured;
const connection = createConnection(args => captured = args);
const gremlinLang = new GremlinLang().addStep('V');

await connection.submit(gremlinLang);

assert.ok(!captured.requestOptions.parameters || !captured.requestOptions.parameters.includes("'g':"));
});

it('sends explicit GremlinLang parameters without adding traversal source', async function () {
let captured;
const connection = createConnection(args => captured = args);
const gremlinLang = new GremlinLang().addStep('V');
gremlinLang.getParameters().set('x', 42);

await connection.submit(gremlinLang);

assert.strictEqual(captured.requestOptions.parameters, "['x':42]");
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ def close(self):

def submit(self, gremlin_lang):
log.debug("submit with gremlin lang script '%s'", gremlin_lang.get_gremlin())
gremlin_lang.add_g(self._traversal_source)
result_set = self._client.submit(gremlin_lang.get_gremlin(),
request_options=self.extract_request_options(gremlin_lang))
return RemoteTraversal(result_set)
Expand All @@ -85,7 +84,6 @@ def submitAsync(self, message, parameters=None, request_options=None):
def submit_async(self, gremlin_lang):
log.debug("submit_async with gremlin lang script '%s'", gremlin_lang.get_gremlin())
future = Future()
gremlin_lang.add_g(self._traversal_source)
future_result_set = self._client.submit_async(gremlin_lang.get_gremlin(),
request_options=self.extract_request_options(gremlin_lang))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,6 @@ def submit(self, gremlin_lang):
if not self._transaction.is_open:
raise Exception("Transaction is not open")

gremlin_lang.add_g(self._traversal_source)

from gremlin_python.driver.driver_remote_connection import DriverRemoteConnection
request_options = DriverRemoteConnection.extract_request_options(gremlin_lang)
request_options['transactionId'] = self._transaction.transaction_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1090,9 +1090,6 @@ def convert_parameters_to_string(params):
parts.append(f'{helper._arg_as_string(k)}:{helper._arg_as_string(v)}')
return '[' + ','.join(parts) + ']'

def add_g(self, g):
self.parameters['g'] = g

def add_source(self, source_name, *args):

if source_name == 'withStrategies' and len(args) != 0:
Expand Down
Loading
Loading