Skip to content

Commit 41c7711

Browse files
authored
GT-510 Fix using VST for database with non-ANSI characters (#563)
1 parent 8c56b71 commit 41c7711

File tree

5 files changed

+58
-27
lines changed

5 files changed

+58
-27
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
- Move examples to separate package
2828
- Deprecate ClientConfig.SynchronizeEndpointsInterval due to bug in implementation
2929
- [V1] Add Rename function for collections (single server only).
30+
- [V1] Fix using VST for database with non-ANSI characters
3031

3132
## [1.6.0](https://github.com/arangodb/go-driver/tree/v1.6.0) (2023-05-30)
3233
- Add ErrArangoDatabaseNotFound and IsExternalStorageError helper to v2

test/collection_test.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package test
2323
import (
2424
"context"
2525
"fmt"
26+
"strconv"
2627
"testing"
2728
"time"
2829

@@ -211,19 +212,10 @@ func TestCollection_ComputedValues(t *testing.T) {
211212
createdAtValue, createdAtIsPresent := readDoc["createdAt"]
212213
require.True(t, createdAtIsPresent)
213214

214-
// Verify that the computed value is a valid date
215-
var createdAtValueInt64 int64
216-
217-
switch cav := createdAtValue.(type) {
218-
case int64:
219-
createdAtValueInt64 = cav
220-
case float64:
221-
createdAtValueInt64 = int64(cav)
222-
case uint64:
223-
createdAtValueInt64 = int64(cav)
224-
default:
225-
t.Fatalf("Unexpected type of createdAt value: %T", createdAtValue)
226-
}
215+
t.Logf("createdAtValue raw value: %v", createdAtValue)
216+
createdAtValueInt64, err := parseInt64FromInterface(createdAtValue)
217+
require.NoError(t, err)
218+
t.Logf("createdAtValue parsed value: %v", createdAtValueInt64)
227219

228220
tm := time.Unix(createdAtValueInt64, 0)
229221
require.True(t, tm.After(time.Now().Add(-time.Second)))
@@ -415,6 +407,23 @@ func TestCreateCollectionWithShardingStrategy(t *testing.T) {
415407
}
416408
}
417409

410+
func parseInt64FromInterface(value interface{}) (int64, error) {
411+
switch v := value.(type) {
412+
case int:
413+
return int64(v), nil
414+
case int8, int16, int32, int64:
415+
return v.(int64), nil
416+
case uint, uint8, uint16, uint32, uint64:
417+
return int64(v.(uint64)), nil
418+
case float32, float64:
419+
return int64(v.(float64)), nil
420+
case string:
421+
return strconv.ParseInt(v, 10, 64)
422+
default:
423+
return 0, fmt.Errorf("value is of type %T, not convertible to int64", v)
424+
}
425+
}
426+
418427
// TestRemoveCollection creates a collection and then removes it.
419428
func TestRemoveCollection(t *testing.T) {
420429
c := createClient(t, nil)

test/database_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"testing"
2828

2929
"github.com/dchest/uniuri"
30+
"github.com/google/uuid"
3031
"github.com/stretchr/testify/require"
3132
"golang.org/x/text/unicode/norm"
3233

@@ -167,7 +168,7 @@ func TestDatabaseNameUnicode(t *testing.T) {
167168
c := createClient(t, nil)
168169
databaseExtendedNamesRequired(t, c)
169170

170-
dbName := "\u006E\u0303\u00f1"
171+
dbName := "\u006E\u0303\u00f1" + uuid.New().String()
171172
normalized := norm.NFC.String(dbName)
172173
ctx := context.Background()
173174
_, err := c.CreateDatabase(ctx, dbName, nil)

v2/tests/database_collection_operations_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ func TestDatabaseNameUnicode(t *testing.T) {
512512
// with the option --database.extended-names-databases=true.
513513
func databaseExtendedNamesRequired(t *testing.T, c arangodb.Client, ctx context.Context) {
514514
// If the database can be created with the below name then it means that it excepts unicode names.
515-
dbName := "\u006E\u0303\u00f1"
515+
dbName := "\u006E\u0303\u00f1" + uuid.New().String()
516516
normalized := norm.NFC.String(dbName)
517517
db, err := c.CreateDatabase(ctx, normalized, nil)
518518
if err == nil {

vst/request.go

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// DISCLAIMER
33
//
4-
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
4+
// Copyright 2017-2023 ArangoDB GmbH, Cologne, Germany
55
//
66
// Licensed under the Apache License, Version 2.0 (the "License");
77
// you may not use this file except in compliance with the License.
@@ -17,8 +17,6 @@
1717
//
1818
// Copyright holder is ArangoDB GmbH, Cologne, Germany
1919
//
20-
// Author Ewout Prangsma
21-
//
2220

2321
package vst
2422

@@ -149,11 +147,17 @@ func (r *vstRequest) createMessageParts() ([][]byte, error) {
149147
if strings.HasPrefix(path, "_db/") {
150148
path = path[4:] // Remove '_db/'
151149
parts := strings.SplitN(path, "/", 2)
150+
151+
// ensure database name is not URL-encoded
152+
dbName, err := url.QueryUnescape(parts[0])
153+
if err != nil {
154+
return nil, driver.WithStack(err)
155+
}
156+
databaseValue = velocypack.NewStringValue(dbName)
157+
152158
if len(parts) == 1 {
153-
databaseValue = velocypack.NewStringValue(parts[0])
154159
path = ""
155160
} else {
156-
databaseValue = velocypack.NewStringValue(parts[0])
157161
path = parts[1]
158162
}
159163
}
@@ -162,18 +166,34 @@ func (r *vstRequest) createMessageParts() ([][]byte, error) {
162166
// Create header
163167
var b velocypack.Builder
164168
b.OpenArray()
165-
b.AddValue(velocypack.NewIntValue(1)) // Version
166-
b.AddValue(velocypack.NewIntValue(1)) // Type (1=Req)
167-
b.AddValue(databaseValue) // Database name
168-
b.AddValue(velocypack.NewIntValue(r.requestType())) // Request type
169-
b.AddValue(velocypack.NewStringValue(path)) // Request
170-
b.OpenObject() // Parameters
169+
170+
// member 0: numeric version of the velocypack protocol. Must always be 1 at the moment.
171+
b.AddValue(velocypack.NewIntValue(1))
172+
173+
// member 1: numeric representation of the VST request type. Must always be 1 at the moment.
174+
b.AddValue(velocypack.NewIntValue(1))
175+
176+
// member 2: string with the database name - this must be the normalized database name, but not URL-encoded in any way!
177+
b.AddValue(databaseValue) // Database name
178+
179+
// member 3: numeric representation of the request type (GET, POST, PUT etc.)
180+
b.AddValue(velocypack.NewIntValue(r.requestType()))
181+
182+
// member 4: string with a relative request path, starting at /
183+
// There is no need for this path to contain the database name, as the database name is already transferred in member 2.
184+
// There is also no need for the path to contain request parameters (e.g. key=value), as they should be transferred in member 5.
185+
b.AddValue(velocypack.NewStringValue(path))
186+
187+
// member 5: object with request parameters (e.g. { "foo": "bar", "baz": "qux" }
188+
b.OpenObject()
171189
for k, v := range r.q {
172190
if len(v) > 0 {
173191
b.AddKeyValue(k, velocypack.NewStringValue(v[0]))
174192
}
175193
}
176-
b.Close() // Parameters
194+
b.Close()
195+
196+
// member 6: object with “HTTP” headers (e.g. { "x-arango-async" : "store" }
177197
b.OpenObject() // Meta
178198
for k, v := range r.hdr {
179199
b.AddKeyValue(k, velocypack.NewStringValue(v))

0 commit comments

Comments
 (0)