diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 19d74303d38..392b04993ed 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -36,6 +36,8 @@ This release also includes changes from <>. * Moves `SimpleSocketServer` and its initializers to a new `gremlin-tools/gremlin-socket-server` module. * Configures `gremlin-socket-server` to build a docker image which can be used for testing GLV's. (Can be skipped with -DskipImageBuild) * Reduces dependency from `gremlin-server` onto `gremlin-driver` to a test scope only. +* Added `RequestOptions` and `RequestOptionsBuilder` types to Go GLV to encapsulate per-request settings and bindings. +* Added `SubmitWithOptions()` methods to `Client` and `DriverRemoteConnection` in Go GLV to pass `RequestOptions` to the server. == TinkerPop 3.6.0 (Tinkerheart) diff --git a/docs/src/reference/gremlin-applications.asciidoc b/docs/src/reference/gremlin-applications.asciidoc index 593931980a2..6d5d2467876 100644 --- a/docs/src/reference/gremlin-applications.asciidoc +++ b/docs/src/reference/gremlin-applications.asciidoc @@ -612,7 +612,8 @@ list = g.V().has("person","name","marko").out("knows").toList() ---- // script client, err := NewClient("ws://localhost:8182/gremlin") -resultSet, err := client.Submit("g.V().has('person','name',name).out('knows')", map[string]interface{}{"name": "marko"}) +resultSet, err := client.SubmitWithOptions("g.V().has('person','name',name).out('knows')", + new(RequestOptionsBuilder).AddBinding("name", "marko").Create()) result, err := resultSet.All() // bytecode diff --git a/docs/src/reference/gremlin-variants.asciidoc b/docs/src/reference/gremlin-variants.asciidoc index fcf8f126b17..2d2fc71a2f6 100644 --- a/docs/src/reference/gremlin-variants.asciidoc +++ b/docs/src/reference/gremlin-variants.asciidoc @@ -293,22 +293,29 @@ fmt.Println(result[0].GetString()) <3> ---- <1> Submit a script that simply returns a Count of vertexes. -<2> Get results from resultSet. Block until the the script is evaluated and results are sent back by the server. +<2> Get results from resultSet. Block until the script is evaluated and results are sent back by the server. <3> Use the result. ==== Per Request Settings -The `client.Submit()` functions accept a `bindings` which expects a map. The `bindings` provide a way to include options -that are specific to the request made with the call to `Submit()`. A good use-case for this feature is to set a per-request -override to the `evaluationTimeout` so that it only applies to the current request. +Both the `Client` and `DriverRemoteConnection` types have a `SubmitWithOptions(traversalString, requestOptions)` variant +of the standard `Submit()` method. These methods allow a `RequestOptions` struct to be passed in which will augment the +execution on the server. `RequestOptions` can be constructed +using `RequestOptionsBuilder`. A good use-case for this feature is to set a per-request override to the +`evaluationTimeout` so that it only applies to the current request. [source,go] ---- -resultSet, err := client.Submit("g.V().repeat(both()).times(100)", map[string]interface{}{"evaluationTimeout": 5000}) +options := new(RequestOptionsBuilder). + SetEvaluationTimeout(5000). + AddBinding("x", 100). + Create() +resultSet, err := client.SubmitWithOptions("g.V(x).count()", options) ---- The following options are allowed on a per-request basis in this fashion: `batchSize`, `requestId`, `userAgent` and -`evaluationTimeout`. +`evaluationTimeout`. `RequestOptions` may also contain a map of variable `bindings` to be applied to the supplied +traversal string. IMPORTANT: The preferred method for setting a per-request timeout for scripts is demonstrated above, but those familiar with bytecode may try `g.with("evaluationTimeout", 500)` within a script. Scripts with multiple traversals and multiple @@ -316,9 +323,9 @@ timeouts will be interpreted as a sum of all timeouts identified in the script f [source,go] ---- -resultSet, err := client.Submit("g.with('evaluationTimeout', 500).addV().iterate();"+ +resultSet, err := client.SubmitWithOptions("g.with('evaluationTimeout', 500).addV().iterate();"+ "g.addV().iterate();"+ - "g.with('evaluationTimeout', 500).addV();", map[string]interface{}{"evaluationTimeout": 500}) + "g.with('evaluationTimeout', 500).addV();", new(RequestOptionsBuilder).SetEvaluationTimeout(500).Create()) results, err := resultSet.All() ---- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Util/SocketServerSettings.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Util/SocketServerSettings.cs index dae1e407826..2ae0e22c7a0 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Util/SocketServerSettings.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Util/SocketServerSettings.cs @@ -87,7 +87,7 @@ public class SocketServerSettings public static SocketServerSettings FromYaml(String path) { - var deserializer = new YamlDotNet.Serialization.DeserializerBuilder().Build(); + var deserializer = new YamlDotNet.Serialization.DeserializerBuilder().IgnoreUnmatchedProperties().Build(); return deserializer.Deserialize(File.ReadAllText(path)); } diff --git a/gremlin-go/driver/client.go b/gremlin-go/driver/client.go index 8e2f2cf7d2b..872db55773d 100644 --- a/gremlin-go/driver/client.go +++ b/gremlin-go/driver/client.go @@ -147,10 +147,10 @@ func (client *Client) Close() { client.connections.close() } -// Submit submits a Gremlin script to the server and returns a ResultSet. -func (client *Client) Submit(traversalString string, bindings ...map[string]interface{}) (ResultSet, error) { +// SubmitWithOptions submits a Gremlin script to the server with specified RequestOptions and returns a ResultSet. +func (client *Client) SubmitWithOptions(traversalString string, requestOptions RequestOptions) (ResultSet, error) { client.logHandler.logf(Debug, submitStartedString, traversalString) - request := makeStringRequest(traversalString, client.traversalSource, client.session, bindings...) + request := makeStringRequest(traversalString, client.traversalSource, client.session, requestOptions) result, err := client.connections.write(&request) if err != nil { client.logHandler.logf(Error, logErrorGeneric, "Client.Submit()", err.Error()) @@ -158,6 +158,17 @@ func (client *Client) Submit(traversalString string, bindings ...map[string]inte return result, err } +// Submit submits a Gremlin script to the server and returns a ResultSet. Submit can optionally accept a map of bindings +// to be applied to the traversalString, it is preferred however to instead wrap any bindings into a RequestOptions +// struct and use SubmitWithOptions(). +func (client *Client) Submit(traversalString string, bindings ...map[string]interface{}) (ResultSet, error) { + requestOptionsBuilder := new(RequestOptionsBuilder) + if len(bindings) > 0 { + requestOptionsBuilder.SetBindings(bindings[0]) + } + return client.SubmitWithOptions(traversalString, requestOptionsBuilder.Create()) +} + // submitBytecode submits Bytecode to the server to execute and returns a ResultSet. func (client *Client) submitBytecode(bytecode *Bytecode) (ResultSet, error) { client.logHandler.logf(Debug, submitStartedBytecode, *bytecode) diff --git a/gremlin-go/driver/client_test.go b/gremlin-go/driver/client_test.go index fdda8eb6144..3e2fd1d2861 100644 --- a/gremlin-go/driver/client_test.go +++ b/gremlin-go/driver/client_test.go @@ -37,13 +37,14 @@ func TestClient(t *testing.T) { testNoAuthAuthInfo := &AuthInfo{} testNoAuthTlsConfig := &tls.Config{} - t.Run("Test client.submit()", func(t *testing.T) { + t.Run("Test client.Submit()", func(t *testing.T) { skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) client, err := NewClient(testNoAuthUrl, func(settings *ClientSettings) { settings.TlsConfig = testNoAuthTlsConfig settings.AuthInfo = testNoAuthAuthInfo }) + defer client.Close() assert.Nil(t, err) assert.NotNil(t, client) resultSet, err := client.Submit("g.V().count()") @@ -53,7 +54,25 @@ func TestClient(t *testing.T) { assert.Nil(t, err) assert.True(t, ok) assert.NotNil(t, result) - client.Close() + }) + + t.Run("Test client.SubmitWithOptions()", func(t *testing.T) { + skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) + client, err := NewClient(testNoAuthUrl, + func(settings *ClientSettings) { + settings.TlsConfig = testNoAuthTlsConfig + settings.AuthInfo = testNoAuthAuthInfo + }) + defer client.Close() + assert.Nil(t, err) + assert.NotNil(t, client) + resultSet, err := client.SubmitWithOptions("g.V().count()", *new(RequestOptions)) + assert.Nil(t, err) + assert.NotNil(t, resultSet) + result, ok, err := resultSet.One() + assert.Nil(t, err) + assert.True(t, ok) + assert.NotNil(t, result) }) } @@ -91,6 +110,12 @@ type SocketServerSettings struct { * during the web socket handshake. */ USER_AGENT_REQUEST_ID uuid.UUID `yaml:"USER_AGENT_REQUEST_ID"` + /** + * If a request with this ID comes to the server, the server responds with a string containing all overridden + * per request settings from the request message. String will be of the form + * "requestId=19436d9e-f8fc-4b67-8a76-deec60918424 evaluationTimeout=1234, batchSize=12, userAgent=testUserAgent" + */ + PER_REQUEST_SETTINGS_REQUEST_ID uuid.UUID `yaml:"PER_REQUEST_SETTINGS_REQUEST_ID"` } func FromYaml(path string) *SocketServerSettings { @@ -121,16 +146,17 @@ func TestClientAgainstSocketServer(t *testing.T) { t.Run("Should get single vertex response from gremlin socket server", func(t *testing.T) { skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) client, err := NewClient(testSocketServerUrl) + defer client.Close() assert.Nil(t, err) assert.NotNil(t, client) - resultSet, err := client.Submit("1", map[string]interface{}{"requestId": settings.SINGLE_VERTEX_REQUEST_ID}) + resultSet, err := client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.SINGLE_VERTEX_REQUEST_ID).Create()) assert.Nil(t, err) assert.NotNil(t, resultSet) result, ok, err := resultSet.One() assert.Nil(t, err) assert.True(t, ok) assert.NotNil(t, result) - client.Close() }) /** @@ -140,10 +166,12 @@ func TestClientAgainstSocketServer(t *testing.T) { t.Run("Should include user agent in handshake request", func(t *testing.T) { skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) client, err := NewClient(testSocketServerUrl) + defer client.Close() assert.Nil(t, err) assert.NotNil(t, client) - resultSet, err := client.Submit("1", map[string]interface{}{"requestId": settings.USER_AGENT_REQUEST_ID}) + resultSet, err := client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.USER_AGENT_REQUEST_ID).Create()) assert.Nil(t, err) assert.NotNil(t, resultSet) @@ -154,8 +182,6 @@ func TestClientAgainstSocketServer(t *testing.T) { userAgentResponse := result.GetString() assert.Equal(t, userAgent, userAgentResponse) - - client.Close() }) /** @@ -168,10 +194,12 @@ func TestClientAgainstSocketServer(t *testing.T) { func(settings *ClientSettings) { settings.EnableUserAgentOnConnect = false }) + defer client.Close() assert.Nil(t, err) assert.NotNil(t, client) - resultSet, err := client.Submit("1", map[string]interface{}{"requestId": settings.USER_AGENT_REQUEST_ID}) + resultSet, err := client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.USER_AGENT_REQUEST_ID).Create()) assert.Nil(t, err) assert.NotNil(t, resultSet) @@ -184,8 +212,33 @@ func TestClientAgainstSocketServer(t *testing.T) { //If the gremlin user agent is disabled, the underlying web socket library reverts to sending its default user agent //during connection requests. assert.Contains(t, userAgentResponse, "Go-http-client/") + }) + + /** + * Tests that client is correctly sending all overridable per request settings (requestId, batchSize, + * evaluationTimeout, and userAgent) to the server. + */ + t.Run("Should Send Per Request Settings To Server", func(t *testing.T) { + skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) + client, err := NewClient(testSocketServerUrl) + defer client.Close() + assert.Nil(t, err) + assert.NotNil(t, client) - client.Close() + resultSet, err := client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.PER_REQUEST_SETTINGS_REQUEST_ID). + SetEvaluationTimeout(1234). + SetBatchSize(12). + SetUserAgent("helloWorld"). + Create()) + assert.Nil(t, err) + assert.NotNil(t, resultSet) + result, ok, err := resultSet.One() + assert.Nil(t, err) + assert.True(t, ok) + expectedResult := fmt.Sprintf("requestId=%v evaluationTimeout=%v, batchSize=%v, userAgent=%v", + settings.PER_REQUEST_SETTINGS_REQUEST_ID, 1234, 12, "helloWorld") + assert.Equal(t, expectedResult, result.Data) }) /** @@ -197,9 +250,11 @@ func TestClientAgainstSocketServer(t *testing.T) { t.Run("Should try create new connection if closed by server", func(t *testing.T) { skipTestsIfNotEnabled(t, integrationTestSuiteName, testNoAuthEnable) client, err := NewClient(testSocketServerUrl) + defer client.Close() assert.Nil(t, err) assert.NotNil(t, client) - resultSet, err := client.Submit("1", map[string]interface{}{"requestId": settings.CLOSE_CONNECTION_REQUEST_ID}) + resultSet, err := client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.CLOSE_CONNECTION_REQUEST_ID).Create()) assert.Nil(t, err) assert.NotNil(t, resultSet) @@ -207,14 +262,14 @@ func TestClientAgainstSocketServer(t *testing.T) { assert.EqualError(t, err, "websocket: close 1005 (no status)") - resultSet, err = client.Submit("1", map[string]interface{}{"requestId": settings.SINGLE_VERTEX_REQUEST_ID}) + resultSet, err = client.SubmitWithOptions("1", new(RequestOptionsBuilder). + SetRequestId(settings.SINGLE_VERTEX_REQUEST_ID).Create()) assert.Nil(t, err) assert.NotNil(t, resultSet) result, ok, err = resultSet.One() assert.Nil(t, err) assert.True(t, ok) assert.NotNil(t, result) - client.Close() }) */ } diff --git a/gremlin-go/driver/connection_test.go b/gremlin-go/driver/connection_test.go index 9c142d8bb3c..b6e0045b2ac 100644 --- a/gremlin-go/driver/connection_test.go +++ b/gremlin-go/driver/connection_test.go @@ -381,7 +381,7 @@ func TestConnection(t *testing.T) { assert.NotNil(t, connection) assert.Equal(t, established, connection.state) defer deferredCleanup(t, connection) - request := makeStringRequest("g.V().count()", "g", "") + request := makeStringRequest("g.V().count()", "g", "", *new(RequestOptions)) resultSet, err := connection.write(&request) assert.Nil(t, err) assert.NotNil(t, resultSet) @@ -399,7 +399,7 @@ func TestConnection(t *testing.T) { assert.NotNil(t, connection) assert.Equal(t, established, connection.state) defer deferredCleanup(t, connection) - request := makeStringRequest("g.V().count()", "g", "") + request := makeStringRequest("g.V().count()", "g", "", *new(RequestOptions)) resultSet, err := connection.write(&request) assert.Nil(t, err) assert.NotNil(t, resultSet) @@ -435,7 +435,7 @@ func TestConnection(t *testing.T) { err = connection.close() assert.Nil(t, err) assert.Equal(t, closed, connection.state) - request := makeStringRequest("g.V().count()", "g", "") + request := makeStringRequest("g.V().count()", "g", "", *new(RequestOptions)) resultSet, err := connection.write(&request) assert.Nil(t, resultSet) assert.Equal(t, newError(err0102WriteConnectionClosedError), err) @@ -451,7 +451,7 @@ func TestConnection(t *testing.T) { assert.Equal(t, established, connection.state) assert.Nil(t, err) time.Sleep(120 * time.Second) - request := makeStringRequest("g.V().count()", "g", "") + request := makeStringRequest("g.V().count()", "g", "", *new(RequestOptions)) resultSet, err := connection.write(&request) assert.Nil(t, resultSet) assert.NotNil(t, err) diff --git a/gremlin-go/driver/driverRemoteConnection.go b/gremlin-go/driver/driverRemoteConnection.go index 4677c6c38d5..cce81dbb251 100644 --- a/gremlin-go/driver/driverRemoteConnection.go +++ b/gremlin-go/driver/driverRemoteConnection.go @@ -163,15 +163,20 @@ func (driver *DriverRemoteConnection) Close() { driver.isClosed = true } -// Submit sends a string traversal to the server. -func (driver *DriverRemoteConnection) Submit(traversalString string) (ResultSet, error) { - result, err := driver.client.Submit(traversalString) +// SubmitWithOptions sends a string traversal to the server along with specified RequestOptions. +func (driver *DriverRemoteConnection) SubmitWithOptions(traversalString string, requestOptions RequestOptions) (ResultSet, error) { + result, err := driver.client.SubmitWithOptions(traversalString, requestOptions) if err != nil { driver.client.logHandler.logf(Error, logErrorGeneric, "Driver.Submit()", err.Error()) } return result, err } +// Submit sends a string traversal to the server. +func (driver *DriverRemoteConnection) Submit(traversalString string) (ResultSet, error) { + return driver.SubmitWithOptions(traversalString, *new(RequestOptions)) +} + // submitBytecode sends a Bytecode traversal to the server. func (driver *DriverRemoteConnection) submitBytecode(bytecode *Bytecode) (ResultSet, error) { if driver.isClosed { diff --git a/gremlin-go/driver/request.go b/gremlin-go/driver/request.go index 304317d9a39..d1cd8a9ee50 100644 --- a/gremlin-go/driver/request.go +++ b/gremlin-go/driver/request.go @@ -20,7 +20,6 @@ under the License. package gremlingo import ( - "fmt" "github.com/google/uuid" ) @@ -37,8 +36,7 @@ const sessionProcessor = "session" const stringOp = "eval" const stringProcessor = "" -// Bindings should be a key-object map (different from Binding class in Bytecode). -func makeStringRequest(stringGremlin string, traversalSource string, sessionId string, bindings ...map[string]interface{}) (req request) { +func makeStringRequest(stringGremlin string, traversalSource string, sessionId string, requestOptions RequestOptions) (req request) { newProcessor := stringProcessor newArgs := map[string]interface{}{ "gremlin": stringGremlin, @@ -50,13 +48,27 @@ func makeStringRequest(stringGremlin string, traversalSource string, sessionId s newProcessor = sessionProcessor newArgs["session"] = sessionId } - requestId := uuid.New() - if len(bindings) > 0 { - newArgs["bindings"] = bindings[0] - customRequestId, err := uuid.Parse(fmt.Sprintf("%v", bindings[0]["requestId"])) - if err == nil { - requestId = customRequestId - } + var requestId uuid.UUID + if requestOptions.requestID == uuid.Nil { + requestId = uuid.New() + } else { + requestId = requestOptions.requestID + } + + if requestOptions.bindings != nil { + newArgs["bindings"] = requestOptions.bindings + } + + if requestOptions.evaluationTimeout != 0 { + newArgs["evaluationTimeout"] = requestOptions.evaluationTimeout + } + + if requestOptions.batchSize != 0 { + newArgs["batchSize"] = requestOptions.batchSize + } + + if requestOptions.userAgent != "" { + newArgs["userAgent"] = requestOptions.userAgent } return request{ diff --git a/gremlin-go/driver/requestOptions.go b/gremlin-go/driver/requestOptions.go new file mode 100644 index 00000000000..4153eb162af --- /dev/null +++ b/gremlin-go/driver/requestOptions.go @@ -0,0 +1,85 @@ +/* +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. +*/ + +package gremlingo + +import ( + "github.com/google/uuid" +) + +type RequestOptions struct { + requestID uuid.UUID + evaluationTimeout int + batchSize int + userAgent string + bindings map[string]interface{} +} + +type RequestOptionsBuilder struct { + requestID uuid.UUID + evaluationTimeout int + batchSize int + userAgent string + bindings map[string]interface{} +} + +func (builder *RequestOptionsBuilder) SetRequestId(requestId uuid.UUID) *RequestOptionsBuilder { + builder.requestID = requestId + return builder +} + +func (builder *RequestOptionsBuilder) SetEvaluationTimeout(evaluationTimeout int) *RequestOptionsBuilder { + builder.evaluationTimeout = evaluationTimeout + return builder +} + +func (builder *RequestOptionsBuilder) SetBatchSize(batchSize int) *RequestOptionsBuilder { + builder.batchSize = batchSize + return builder +} + +func (builder *RequestOptionsBuilder) SetUserAgent(userAgent string) *RequestOptionsBuilder { + builder.userAgent = userAgent + return builder +} + +func (builder *RequestOptionsBuilder) SetBindings(bindings map[string]interface{}) *RequestOptionsBuilder { + builder.bindings = bindings + return builder +} + +func (builder *RequestOptionsBuilder) AddBinding(key string, binding interface{}) *RequestOptionsBuilder { + if builder.bindings == nil { + builder.bindings = make(map[string]interface{}) + } + builder.bindings[key] = binding + return builder +} + +func (builder *RequestOptionsBuilder) Create() RequestOptions { + requestOptions := new(RequestOptions) + + requestOptions.requestID = builder.requestID + requestOptions.evaluationTimeout = builder.evaluationTimeout + requestOptions.batchSize = builder.batchSize + requestOptions.userAgent = builder.userAgent + requestOptions.bindings = builder.bindings + + return *requestOptions +} diff --git a/gremlin-go/driver/requestOptions_test.go b/gremlin-go/driver/requestOptions_test.go new file mode 100644 index 00000000000..6eb6259262a --- /dev/null +++ b/gremlin-go/driver/requestOptions_test.go @@ -0,0 +1,75 @@ +/* +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. +*/ + +package gremlingo + +import ( + "github.com/google/uuid" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRequestOptions(t *testing.T) { + t.Run("Test RequestOptionsBuilder with custom requestID", func(t *testing.T) { + requestId := uuid.New() + r := new(RequestOptionsBuilder).SetRequestId(requestId).Create() + assert.Equal(t, requestId, r.requestID) + }) + t.Run("Test RequestOptionsBuilder with custom evaluationTimeout", func(t *testing.T) { + r := new(RequestOptionsBuilder).SetEvaluationTimeout(1234).Create() + assert.Equal(t, 1234, r.evaluationTimeout) + }) + t.Run("Test RequestOptionsBuilder with custom batchSize", func(t *testing.T) { + r := new(RequestOptionsBuilder).SetBatchSize(123).Create() + assert.Equal(t, 123, r.batchSize) + }) + t.Run("Test RequestOptionsBuilder with custom userAgent", func(t *testing.T) { + r := new(RequestOptionsBuilder).SetUserAgent("TestUserAgent").Create() + assert.Equal(t, "TestUserAgent", r.userAgent) + }) + t.Run("Test RequestOptionsBuilder with custom bindings", func(t *testing.T) { + bindings := map[string]interface{}{"x": 2, "y": 5} + r := new(RequestOptionsBuilder).SetBindings(bindings).Create() + assert.Equal(t, bindings, r.bindings) + }) + t.Run("Test RequestOptionsBuilder AddBinding() with no other bindings", func(t *testing.T) { + r := new(RequestOptionsBuilder).AddBinding("x", 2).AddBinding("y", 5).Create() + expectedBindings := map[string]interface{}{"x": 2, "y": 5} + assert.Equal(t, expectedBindings, r.bindings) + }) + t.Run("Test RequestOptionsBuilder AddBinding() overwriting existing key", func(t *testing.T) { + r := new(RequestOptionsBuilder).AddBinding("x", 2).AddBinding("x", 5).Create() + expectedBindings := map[string]interface{}{"x": 5} + assert.Equal(t, expectedBindings, r.bindings) + }) + t.Run("Test RequestOptionsBuilder AddBinding() with existing bindings", func(t *testing.T) { + bindings := map[string]interface{}{"x": 2, "y": 5} + r := new(RequestOptionsBuilder).SetBindings(bindings).AddBinding("z", 7).Create() + expectedBindings := map[string]interface{}{"x": 2, "y": 5, "z": 7} + assert.Equal(t, expectedBindings, r.bindings) + }) + t.Run("Test RequestOptionsBuilder SetBinding(...), SetBinding(nil), AddBinding(...)", func(t *testing.T) { + bindings := map[string]interface{}{"x": 2, "y": 5} + r := new(RequestOptionsBuilder).SetBindings(bindings). + SetBindings(nil).AddBinding("z", 7).Create() + expectedBindings := map[string]interface{}{"z": 7} + assert.Equal(t, expectedBindings, r.bindings) + }) +} diff --git a/gremlin-go/driver/request_test.go b/gremlin-go/driver/request_test.go index 0e1d8433e83..c388a1273e2 100644 --- a/gremlin-go/driver/request_test.go +++ b/gremlin-go/driver/request_test.go @@ -20,7 +20,6 @@ under the License. package gremlingo import ( - "fmt" "github.com/google/uuid" "testing" @@ -29,22 +28,39 @@ import ( func TestRequest(t *testing.T) { t.Run("Test makeStringRequest() with custom requestID", func(t *testing.T) { - requestId := fmt.Sprintf("%v", uuid.New()) - r := makeStringRequest("g.V()", "g", "", map[string]interface{}{"requestId": requestId}) - assert.Equal(t, requestId, fmt.Sprintf("%v", r.requestID)) + requestId := uuid.New() + r := makeStringRequest("g.V()", "g", "", + new(RequestOptionsBuilder).SetRequestId(requestId).Create()) + assert.Equal(t, requestId, r.requestID) }) t.Run("Test makeStringRequest() with no bindings", func(t *testing.T) { - r := makeStringRequest("g.V()", "g", "") + r := makeStringRequest("g.V()", "g", "", *new(RequestOptions)) assert.NotNil(t, r.requestID) assert.NotEqual(t, uuid.Nil, r.requestID) }) t.Run("Test makeStringRequest() with custom evaluationTimeout", func(t *testing.T) { - r := makeStringRequest("g.V()", "g", "", map[string]interface{}{"evaluationTimeout": 1234}) + r := makeStringRequest("g.V()", "g", "", + new(RequestOptionsBuilder).SetEvaluationTimeout(1234).Create()) assert.NotNil(t, r.requestID) assert.NotEqual(t, uuid.Nil, r.requestID) - bindings := r.args["bindings"].(map[string]interface{}) - assert.Equal(t, 1234, bindings["evaluationTimeout"]) + assert.Equal(t, 1234, r.args["evaluationTimeout"]) + }) + + t.Run("Test makeStringRequest() with custom batchSize", func(t *testing.T) { + r := makeStringRequest("g.V()", "g", "", + new(RequestOptionsBuilder).SetBatchSize(123).Create()) + assert.NotNil(t, r.requestID) + assert.NotEqual(t, uuid.Nil, r.requestID) + assert.Equal(t, 123, r.args["batchSize"]) + }) + + t.Run("Test makeStringRequest() with custom userAgent", func(t *testing.T) { + r := makeStringRequest("g.V()", "g", "", + new(RequestOptionsBuilder).SetUserAgent("TestUserAgent").Create()) + assert.NotNil(t, r.requestID) + assert.NotEqual(t, uuid.Nil, r.requestID) + assert.Equal(t, "TestUserAgent", r.args["userAgent"]) }) }