This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch TINKERPOP-2420 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 637be9253bcf0a5eaf1dda47663eb3e770b4042f Author: Stephen Mallette <stepm...@amazon.com> AuthorDate: Fri Oct 2 16:52:39 2020 -0400 TINKERPOP-2420 Added with() support for request options in C# --- gremlin-dotnet/glv/GraphTraversalSource.template | 24 +++++++++++++ gremlin-dotnet/glv/generate.groovy | 1 + .../Driver/Remote/DriverRemoteConnection.cs | 30 ++++++++++++++-- gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs | 28 ++++++++++----- .../Process/Traversal/GraphTraversalSource.cs | 26 +++++++++----- .../DriverRemoteConnection/GraphTraversalTests.cs | 14 ++++---- .../apache/tinkerpop/gremlin/driver/Tokens.java | 40 ++++++++++++++++++++++ 7 files changed, 137 insertions(+), 26 deletions(-) diff --git a/gremlin-dotnet/glv/GraphTraversalSource.template b/gremlin-dotnet/glv/GraphTraversalSource.template index f3093e5..4ccf77f 100644 --- a/gremlin-dotnet/glv/GraphTraversalSource.template +++ b/gremlin-dotnet/glv/GraphTraversalSource.template @@ -72,6 +72,30 @@ namespace Gremlin.Net.Process.Traversal Bytecode = bytecode; } + public GraphTraversalSource With(string key) + { + return With(key, true); + } + + public GraphTraversalSource With(string key, object value) + { + var optionsStrategyInst = Bytecode.SourceInstructions.Find( + inst => inst.OperatorName == "withStrategies" && inst.Arguments[0] is OptionsStrategy); + OptionsStrategy optionsStrategy; + + if (optionsStrategyInst == null) + { + optionsStrategy = new OptionsStrategy(); + optionsStrategy.Configuration[key] = value; + return WithStrategies(optionsStrategy); + } + + optionsStrategy = optionsStrategyInst.Arguments[0]; + optionsStrategy.Configuration[key] = value; + return new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); + } + <% sourceStepMethods.each{ method -> %> public GraphTraversalSource <%= toCSharpMethodName.call(method.methodName) %>(<%= method.parameters %>) { diff --git a/gremlin-dotnet/glv/generate.groovy b/gremlin-dotnet/glv/generate.groovy index 26daa58..a951907 100644 --- a/gremlin-dotnet/glv/generate.groovy +++ b/gremlin-dotnet/glv/generate.groovy @@ -256,6 +256,7 @@ def binding = ["pmethods": P.class.getMethods(). findAll { GraphTraversalSource.class.equals(it.returnType) }. findAll { !it.name.equals("clone") && + !it.name.equals(TraversalSource.Symbols.with) && !it.name.equals(TraversalSource.Symbols.withRemote) && !it.name.equals(TraversalSource.Symbols.withComputer) }. diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs index a52cb87..510a2f4 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Remote/DriverRemoteConnection.cs @@ -27,6 +27,7 @@ using System.Threading.Tasks; using Gremlin.Net.Driver.Messages; using Gremlin.Net.Process.Remote; using Gremlin.Net.Process.Traversal; +using Gremlin.Net.Process.Traversal.Strategy.Decoration; namespace Gremlin.Net.Driver.Remote { @@ -37,6 +38,15 @@ namespace Gremlin.Net.Driver.Remote { private readonly IGremlinClient _client; private readonly string _traversalSource; + + /// <summary> + /// Filter on these keys provided to OptionsStrategy and apply them to the request. Note that + /// "scriptEvaluationTimeout" was deprecated in 3.3.9 but still supported in server implementations and will + /// be removed in later versions. + /// </summary> + private readonly List<String> _allowedKeys = new List<string> + {Tokens.ArgsEvalTimeout, "scriptEvaluationTimeout", Tokens.ArgsBatchSize, + Tokens.RequestId, Tokens.ArgsUserAgent}; /// <summary> /// Initializes a new <see cref="IRemoteConnection" /> using "g" as the default remote TraversalSource name. @@ -78,9 +88,23 @@ namespace Gremlin.Net.Driver.Remote .Processor(Tokens.ProcessorTraversal) .OverrideRequestId(requestid) .AddArgument(Tokens.ArgsGremlin, bytecode) - .AddArgument(Tokens.ArgsAliases, new Dictionary<string, string> {{"g", _traversalSource}}) - .Create(); - return await _client.SubmitAsync<Traverser>(requestMsg).ConfigureAwait(false); + .AddArgument(Tokens.ArgsAliases, new Dictionary<string, string> {{"g", _traversalSource}}); + + var optionsStrategyInst = bytecode.SourceInstructions.Find( + s => s.OperatorName == "withStrategies" && s.Arguments[0] is OptionsStrategy); + if (optionsStrategyInst != null) + { + OptionsStrategy optionsStrategy = optionsStrategyInst.Arguments[0]; + foreach (KeyValuePair<string,dynamic> pair in optionsStrategy.Configuration) + { + if (_allowedKeys.Contains(pair.Key)) + { + requestMsg.AddArgument(pair.Key, pair.Value); + } + } + } + + return await _client.SubmitAsync<Traverser>(requestMsg.Create()).ConfigureAwait(false); } /// <inheritdoc /> diff --git a/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs b/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs index a7f804a..36f281f 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Driver/Tokens.cs @@ -32,6 +32,11 @@ namespace Gremlin.Net.Driver public class Tokens { /// <summary> + /// The key for the unique identifier of the request. + /// </summary> + public static string RequestId = "requestId"; + + /// <summary> /// Operation used by the client to authenticate itself. /// </summary> public static string OpsAuthentication = "authentication"; @@ -74,24 +79,25 @@ namespace Gremlin.Net.Driver public static string ProcessorSession = "session"; /// <summary> - /// Argument name that allows to defines the number of iterations each ResponseMessage should contain - overrides the - /// resultIterationBatchSize server setting. + /// Argument name that allows to definition of the number of iterations each ResponseMessage should + /// contain - overrides the resultIterationBatchSize server setting. /// </summary> public static string ArgsBatchSize = "batchSize"; /// <summary> - /// Argument name that allows to provide a map of key/value pairs to apply as variables in the context of the Gremlin - /// script. + /// Argument name that allows definition of a map of key/value pairs to apply as variables in the + /// context of the Gremlin request sent to the server. /// </summary> public static string ArgsBindings = "bindings"; /// <summary> - /// Argument name that allows to define aliases that represent globally bound Graph and TraversalSource objects. + /// Argument name that allows definition of alias names for Graph and TraversalSource objects on the + /// remote system. /// </summary> public static string ArgsAliases = "aliases"; /// <summary> - /// Argument name that corresponds to the Traversal to evaluate. + /// Argument name that corresponds to the Gremlin to evaluate. /// </summary> public static string ArgsGremlin = "gremlin"; @@ -101,6 +107,12 @@ namespace Gremlin.Net.Driver public static string ArgsSession = "session"; /// <summary> + /// Argument name that allows a value that is a custom string that the user can pass to a server that + /// might accept it for purpose of identifying the kind of client it came from. + /// </summary> + public static string ArgsUserAgent = "userAgent"; + + /// <summary> /// Argument name that allows to specify the unique identifier for the request. /// </summary> [Obsolete("As of release 3.3.8, not replaced, prefer use of cap()-step to retrieve side-effects as part of traversal iteration", false)] @@ -119,12 +131,12 @@ namespace Gremlin.Net.Driver public static string ArgsAggregateTo = "aggregateTo"; /// <summary> - /// Argument name that allows to change the flavor of Gremlin used (e.g. gremlin-groovy). + /// Argument name that allows definition of the flavor of Gremlin used (e.g. gremlin-groovy) to process the request. /// </summary> public static string ArgsLanguage = "language"; /// <summary> - /// Argument name that allows to override the server setting that determines the maximum time to wait for a + /// Argument name that allows the override of the server setting that determines the maximum time to wait for a /// request to execute on the server. /// </summary> public static string ArgsEvalTimeout = "evaluationTimeout"; diff --git a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs index b52b898..7a7f643 100644 --- a/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs +++ b/gremlin-dotnet/src/Gremlin.Net/Process/Traversal/GraphTraversalSource.cs @@ -72,23 +72,31 @@ namespace Gremlin.Net.Process.Traversal Bytecode = bytecode; } - public GraphTraversalSource With(string key) { - var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), - new Bytecode(Bytecode)); - source.Bytecode.AddSource("with", key); - return source; + return With(key, true); } public GraphTraversalSource With(string key, object value) { - var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), - new Bytecode(Bytecode)); - source.Bytecode.AddSource("with", key, value); - return source; + var optionsStrategyInst = Bytecode.SourceInstructions.Find( + inst => inst.OperatorName == "withStrategies" && inst.Arguments[0] is OptionsStrategy); + OptionsStrategy optionsStrategy; + + if (optionsStrategyInst == null) + { + optionsStrategy = new OptionsStrategy(); + optionsStrategy.Configuration[key] = value; + return WithStrategies(optionsStrategy); + } + + optionsStrategy = optionsStrategyInst.Arguments[0]; + optionsStrategy.Configuration[key] = value; + return new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), + new Bytecode(Bytecode)); } + public GraphTraversalSource WithBulk(bool useBulk) { var source = new GraphTraversalSource(new List<ITraversalStrategy>(TraversalStrategies), diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs index 0f6bea6..0ae6277 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Process/Traversal/DriverRemoteConnection/GraphTraversalTests.cs @@ -24,6 +24,9 @@ using System.Linq; using System.Collections.Generic; using System.Threading.Tasks; +using Gremlin.Net.Driver; +using Gremlin.Net.Driver.Exceptions; +using Gremlin.Net.Driver.Messages; using Gremlin.Net.Process.Traversal; using Gremlin.Net.Structure; using Xunit; @@ -201,8 +204,6 @@ namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection [Fact] public void ShouldUseOptionsInTraversal() { - // smoke test to validate serialization of OptionsStrategy. no way to really validate this from an integration - // test perspective because there's no way to access the internals of the strategy via bytecode var connection = _connectionFactory.CreateRemoteConnection(); var options = new Dictionary<string,object> { @@ -210,13 +211,14 @@ namespace Gremlin.Net.IntegrationTest.Process.Traversal.DriverRemoteConnection {"y", true} }; var g = AnonymousTraversalSource.Traversal().WithRemote(connection); - - var b = new Bindings(); + var countWithStrategy = g.WithStrategies(new OptionsStrategy(options)).V().Count().Next(); Assert.Equal(6, countWithStrategy); + + Assert.Equal(ResponseStatusCode.ServerTimeout, Assert.Throws<ResponseException>(() => + g.With("y").With("x", "test").With(Tokens.ArgsEvalTimeout, 10).Inject(1) + .SideEffect(Lambda.Groovy("Thread.sleep(10000)")).Iterate()).StatusCode); - var countWith = g.With("x", "test").With("y", true).V().Count().Next(); - Assert.Equal(6, countWith); } [Fact] diff --git a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java index d7a4215..92641a7 100644 --- a/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java +++ b/gremlin-driver/src/main/java/org/apache/tinkerpop/gremlin/driver/Tokens.java @@ -18,6 +18,10 @@ */ package org.apache.tinkerpop.gremlin.driver; +import org.apache.tinkerpop.gremlin.driver.message.ResponseMessage; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; +import org.apache.tinkerpop.gremlin.structure.Graph; + /** * String constants used in gremlin-driver and gremlin-server. * @@ -46,12 +50,38 @@ public final class Tokens { @Deprecated public static final String OPS_KEYS = "keys"; + /** + * The key for the unique identifier of the request. + */ public static final String REQUEST_ID = "requestId"; + + /** + * Argument name that allows definition of the number of iterations each {@link ResponseMessage} should contain - + * overrides the @{code resultIterationBatchSize} server setting. + */ public static final String ARGS_BATCH_SIZE = "batchSize"; + + /** + * Argument name that allows to provide a map of key/value pairs to apply as variables in the context of + * the Gremlin request sent to the server. + */ public static final String ARGS_BINDINGS = "bindings"; + + /** + * Argument name that allows definition of alias names for {@link Graph} and {@link TraversalSource} objects on + * the remote system. + */ public static final String ARGS_ALIASES = "aliases"; public static final String ARGS_FORCE = "force"; + + /** + * Argument name that corresponds to the Gremlin to evaluate. + */ public static final String ARGS_GREMLIN = "gremlin"; + + /** + * Argument name that allows definition of the flavor of Gremlin used (e.g. gremlin-groovy) to process the request. + */ public static final String ARGS_LANGUAGE = "language"; /** @@ -59,6 +89,11 @@ public final class Tokens { */ @Deprecated public static final String ARGS_SCRIPT_EVAL_TIMEOUT = "scriptEvaluationTimeout"; + + /** + * Argument name that allows the override of the server setting that determines the maximum time to wait for a + * request to execute on the server. + */ public static final String ARGS_EVAL_TIMEOUT = "evaluationTimeout"; public static final String ARGS_HOST = "host"; public static final String ARGS_SESSION = "session"; @@ -86,6 +121,11 @@ public final class Tokens { */ @Deprecated public static final String ARGS_SIDE_EFFECT_KEY = "sideEffectKey"; + + /** + * A value that is a custom string that the user can pass to a server that might accept it for purpose of + * identifying the kind of client it came from. + */ public static final String ARGS_USER_AGENT = "userAgent"; /**