mirror of
https://github.com/arangodb/kube-arangodb.git
synced 2024-12-14 11:57:37 +00:00
Updated Go-Driver to latest version.
This commit is contained in:
parent
06d4af4fe8
commit
c4623b5258
40 changed files with 1886 additions and 212 deletions
File diff suppressed because one or more lines are too long
8
deps/github.com/arangodb/go-driver/.envrc
vendored
8
deps/github.com/arangodb/go-driver/.envrc
vendored
|
@ -1,8 +0,0 @@
|
|||
export GOBUILDDIR=$(pwd)/.gobuild
|
||||
export GOPATH=$GOBUILDDIR:$GOPATH
|
||||
PATH_add $GOBUILDDIR/bin
|
||||
|
||||
if [ ! -e ${GOBUILDDIR} ]; then
|
||||
mkdir -p ${GOBUILDDIR}/src/github.com/arangodb/
|
||||
ln -s ../../../.. ${GOBUILDDIR}/src/github.com/arangodb/go-driver
|
||||
fi
|
|
@ -1 +0,0 @@
|
|||
.gobuild
|
14
deps/github.com/arangodb/go-driver/.travis.yml
vendored
14
deps/github.com/arangodb/go-driver/.travis.yml
vendored
|
@ -1,14 +0,0 @@
|
|||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
language: go
|
||||
|
||||
env:
|
||||
- TEST_SUITE=run-tests-http
|
||||
- TEST_SUITE=run-tests-single ARANGODB=arangodb:3.2
|
||||
- TEST_SUITE=run-tests-single ARANGODB=arangodb/arangodb:latest
|
||||
- TEST_SUITE=run-tests-single ARANGODB=arangodb/arangodb-preview:latest
|
||||
|
||||
script: make $TEST_SUITE
|
|
@ -1,37 +0,0 @@
|
|||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"fileHeaderComment.parameter":{
|
||||
"*":{
|
||||
"commentprefix": "//",
|
||||
"company": "ArangoDB GmbH, Cologne, Germany",
|
||||
"author": "Ewout Prangsma"
|
||||
}
|
||||
},
|
||||
"fileHeaderComment.template":{
|
||||
"*":[
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} DISCLAIMER",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} Copyright ${year} ArangoDB GmbH, Cologne, Germany",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} Licensed under the Apache License, Version 2.0 (the \"License\");",
|
||||
"${commentprefix} you may not use this file except in compliance with the License.",
|
||||
"${commentprefix} You may obtain a copy of the License at",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} http://www.apache.org/licenses/LICENSE-2.0",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} Unless required by applicable law or agreed to in writing, software",
|
||||
"${commentprefix} distributed under the License is distributed on an \"AS IS\" BASIS,",
|
||||
"${commentprefix} WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.",
|
||||
"${commentprefix} See the License for the specific language governing permissions and",
|
||||
"${commentprefix} limitations under the License.",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} Copyright holder is ArangoDB GmbH, Cologne, Germany",
|
||||
"${commentprefix} ",
|
||||
"${commentprefix} Author ${author}",
|
||||
"${commentprefix} ",
|
||||
""
|
||||
]
|
||||
},
|
||||
"go.gopath": "${workspaceRoot}/.gobuild"
|
||||
}
|
11
deps/github.com/arangodb/go-driver/client.go
vendored
11
deps/github.com/arangodb/go-driver/client.go
vendored
|
@ -40,6 +40,17 @@ type Client interface {
|
|||
// This function requires ArangoDB 3.1.15 or up.
|
||||
SynchronizeEndpoints(ctx context.Context) error
|
||||
|
||||
// SynchronizeEndpoints2 fetches all endpoints from an ArangoDB cluster and updates the
|
||||
// connection to use those endpoints.
|
||||
// When this client is connected to a single server, nothing happens.
|
||||
// When this client is connected to a cluster of servers, the connection will be updated to reflect
|
||||
// the layout of the cluster.
|
||||
// Compared to SynchronizeEndpoints, this function expects a database name as additional parameter.
|
||||
// This database name is used to call `_db/<dbname>/_api/cluster/endpoints`. SynchronizeEndpoints uses
|
||||
// the default database, i.e. `_system`. In the case the user does not have access to `_system`,
|
||||
// SynchronizeEndpoints does not work with earlier versions of arangodb.
|
||||
SynchronizeEndpoints2(ctx context.Context, dbname string) error
|
||||
|
||||
// Connection returns the connection used by this client
|
||||
Connection() Connection
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ package driver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/arangodb/go-driver/util"
|
||||
|
@ -67,19 +68,28 @@ func (c *client) Connection() Connection {
|
|||
// When this client is connected to a cluster of servers, the connection will be updated to reflect
|
||||
// the layout of the cluster.
|
||||
func (c *client) SynchronizeEndpoints(ctx context.Context) error {
|
||||
role, err := c.ServerRole(ctx)
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
if role == ServerRoleSingle {
|
||||
// Standalone server, do nothing
|
||||
return nil
|
||||
}
|
||||
return c.SynchronizeEndpoints2(ctx, "")
|
||||
}
|
||||
|
||||
// SynchronizeEndpoints2 fetches all endpoints from an ArangoDB cluster and updates the
|
||||
// connection to use those endpoints.
|
||||
// When this client is connected to a single server, nothing happens.
|
||||
// When this client is connected to a cluster of servers, the connection will be updated to reflect
|
||||
// the layout of the cluster.
|
||||
// Compared to SynchronizeEndpoints, this function expects a database name as additional parameter.
|
||||
// This database name is used to call `_db/<dbname>/_api/cluster/endpoints`. SynchronizeEndpoints uses
|
||||
// the default database, i.e. `_system`. In the case the user does not have access to `_system`,
|
||||
// SynchronizeEndpoints does not work with earlier versions of arangodb.
|
||||
func (c *client) SynchronizeEndpoints2(ctx context.Context, dbname string) error {
|
||||
// Cluster mode, fetch endpoints
|
||||
cep, err := c.clusterEndpoints(ctx)
|
||||
cep, err := c.clusterEndpoints(ctx, dbname)
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
// ignore Forbidden: automatic failover is not enabled errors
|
||||
if !IsArangoErrorWithErrorNum(err, 403, 0, 11) { // 3.2 returns no error code, thus check for 0
|
||||
return WithStack(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
var endpoints []string
|
||||
for _, ep := range cep.Endpoints {
|
||||
|
@ -114,8 +124,14 @@ type clusterEndpoint struct {
|
|||
}
|
||||
|
||||
// clusterEndpoints returns the endpoints of a cluster.
|
||||
func (c *client) clusterEndpoints(ctx context.Context) (clusterEndpointsResponse, error) {
|
||||
req, err := c.conn.NewRequest("GET", "_api/cluster/endpoints")
|
||||
func (c *client) clusterEndpoints(ctx context.Context, dbname string) (clusterEndpointsResponse, error) {
|
||||
var url string
|
||||
if dbname == "" {
|
||||
url = "_api/cluster/endpoints"
|
||||
} else {
|
||||
url = path.Join("_db", pathEscape(dbname), "_api/cluster/endpoints")
|
||||
}
|
||||
req, err := c.conn.NewRequest("GET", url)
|
||||
if err != nil {
|
||||
return clusterEndpointsResponse{}, WithStack(err)
|
||||
}
|
||||
|
|
28
deps/github.com/arangodb/go-driver/cluster.go
vendored
28
deps/github.com/arangodb/go-driver/cluster.go
vendored
|
@ -75,6 +75,8 @@ type ServerHealth struct {
|
|||
Status ServerStatus `json:"Status"`
|
||||
CanBeDeleted bool `json:"CanBeDeleted"`
|
||||
HostID string `json:"Host,omitempty"`
|
||||
Version Version `json:"Version,omitempty"`
|
||||
Engine EngineType `json:"Engine,omitempty"`
|
||||
}
|
||||
|
||||
// ServerStatus describes the health status of a server
|
||||
|
@ -93,6 +95,8 @@ const (
|
|||
type DatabaseInventory struct {
|
||||
// Details of all collections
|
||||
Collections []InventoryCollection `json:"collections,omitempty"`
|
||||
// Details of all views
|
||||
Views []InventoryView `json:"views,omitempty"`
|
||||
}
|
||||
|
||||
// IsReady returns true if the IsReady flag of all collections is set.
|
||||
|
@ -124,6 +128,17 @@ func (i DatabaseInventory) CollectionByName(name string) (InventoryCollection, b
|
|||
return InventoryCollection{}, false
|
||||
}
|
||||
|
||||
// ViewByName returns the InventoryView with given name.
|
||||
// Return false if not found.
|
||||
func (i DatabaseInventory) ViewByName(name string) (InventoryView, bool) {
|
||||
for _, v := range i.Views {
|
||||
if v.Name == name {
|
||||
return v, true
|
||||
}
|
||||
}
|
||||
return InventoryView{}, false
|
||||
}
|
||||
|
||||
// InventoryCollection is a single element of a DatabaseInventory, containing all information
|
||||
// of a specific collection.
|
||||
type InventoryCollection struct {
|
||||
|
@ -196,6 +211,19 @@ func (i InventoryIndex) FieldsEqual(fields []string) bool {
|
|||
return stringSliceEqualsIgnoreOrder(i.Fields, fields)
|
||||
}
|
||||
|
||||
// InventoryView is a single element of a DatabaseInventory, containing all information
|
||||
// of a specific view.
|
||||
type InventoryView struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Deleted bool `json:"deleted,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
IsSystem bool `json:"isSystem,omitempty"`
|
||||
PlanID string `json:"planId,omitempty"`
|
||||
Type ViewType `json:"type,omitempty"`
|
||||
// Include all properties from an arangosearch view.
|
||||
ArangoSearchViewProperties
|
||||
}
|
||||
|
||||
// stringSliceEqualsIgnoreOrder returns true when the given lists contain the same elements.
|
||||
// The order of elements is irrelevant.
|
||||
func stringSliceEqualsIgnoreOrder(a, b []string) bool {
|
||||
|
|
|
@ -59,6 +59,9 @@ func (c *collection) ReadDocument(ctx context.Context, key string, result interf
|
|||
if err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
// This line introduces a lot of side effects. In particular If-Match headers are now set (which is a bugfix)
|
||||
// and invalid query parameters like waitForSync (which is potentially breaking change)
|
||||
cs := applyContextSettings(ctx, req)
|
||||
resp, err := c.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
|
@ -71,6 +74,8 @@ func (c *collection) ReadDocument(ctx context.Context, key string, result interf
|
|||
if err := resp.ParseBody("", &meta); err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
// load context response values
|
||||
loadContextResponseValues(cs, resp)
|
||||
// Parse result
|
||||
if result != nil {
|
||||
if err := resp.ParseBody("", result); err != nil {
|
||||
|
@ -120,6 +125,8 @@ func (c *collection) ReadDocuments(ctx context.Context, keys []string, results i
|
|||
if err := resp.CheckStatus(200); err != nil {
|
||||
return nil, nil, WithStack(err)
|
||||
}
|
||||
// load context response values
|
||||
loadContextResponseValues(cs, resp)
|
||||
// Parse response array
|
||||
metas, errs, err := parseResponseArray(resp, resultCount, cs, results)
|
||||
if err != nil {
|
||||
|
|
|
@ -38,8 +38,13 @@ type indexData struct {
|
|||
MinLength int `json:"minLength,omitempty"`
|
||||
}
|
||||
|
||||
type genericIndexData struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type indexListResponse struct {
|
||||
Indexes []indexData `json:"indexes,omitempty"`
|
||||
Indexes []genericIndexData `json:"indexes,omitempty"`
|
||||
}
|
||||
|
||||
// Index opens a connection to an existing index within the collection.
|
||||
|
@ -60,7 +65,7 @@ func (c *collection) Index(ctx context.Context, name string) (Index, error) {
|
|||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
idx, err := newIndex(data.ID, c)
|
||||
idx, err := newIndex(data.ID, data.Type, c)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
|
@ -106,7 +111,7 @@ func (c *collection) Indexes(ctx context.Context) ([]Index, error) {
|
|||
}
|
||||
result := make([]Index, 0, len(data.Indexes))
|
||||
for _, x := range data.Indexes {
|
||||
idx, err := newIndex(x.ID, c)
|
||||
idx, err := newIndex(x.ID, x.Type, c)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
|
@ -121,7 +126,7 @@ func (c *collection) Indexes(ctx context.Context) ([]Index, error) {
|
|||
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
|
||||
func (c *collection) EnsureFullTextIndex(ctx context.Context, fields []string, options *EnsureFullTextIndexOptions) (Index, bool, error) {
|
||||
input := indexData{
|
||||
Type: "fulltext",
|
||||
Type: string(FullTextIndex),
|
||||
Fields: fields,
|
||||
}
|
||||
if options != nil {
|
||||
|
@ -146,7 +151,7 @@ func (c *collection) EnsureFullTextIndex(ctx context.Context, fields []string, o
|
|||
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
|
||||
func (c *collection) EnsureGeoIndex(ctx context.Context, fields []string, options *EnsureGeoIndexOptions) (Index, bool, error) {
|
||||
input := indexData{
|
||||
Type: "geo",
|
||||
Type: string(GeoIndex),
|
||||
Fields: fields,
|
||||
}
|
||||
if options != nil {
|
||||
|
@ -164,7 +169,7 @@ func (c *collection) EnsureGeoIndex(ctx context.Context, fields []string, option
|
|||
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
|
||||
func (c *collection) EnsureHashIndex(ctx context.Context, fields []string, options *EnsureHashIndexOptions) (Index, bool, error) {
|
||||
input := indexData{
|
||||
Type: "hash",
|
||||
Type: string(HashIndex),
|
||||
Fields: fields,
|
||||
}
|
||||
off := false
|
||||
|
@ -187,7 +192,7 @@ func (c *collection) EnsureHashIndex(ctx context.Context, fields []string, optio
|
|||
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
|
||||
func (c *collection) EnsurePersistentIndex(ctx context.Context, fields []string, options *EnsurePersistentIndexOptions) (Index, bool, error) {
|
||||
input := indexData{
|
||||
Type: "persistent",
|
||||
Type: string(PersistentIndex),
|
||||
Fields: fields,
|
||||
}
|
||||
if options != nil {
|
||||
|
@ -206,7 +211,7 @@ func (c *collection) EnsurePersistentIndex(ctx context.Context, fields []string,
|
|||
// The index is returned, together with a boolean indicating if the index was newly created (true) or pre-existing (false).
|
||||
func (c *collection) EnsureSkipListIndex(ctx context.Context, fields []string, options *EnsureSkipListIndexOptions) (Index, bool, error) {
|
||||
input := indexData{
|
||||
Type: "skiplist",
|
||||
Type: string(SkipListIndex),
|
||||
Fields: fields,
|
||||
}
|
||||
off := false
|
||||
|
@ -248,7 +253,7 @@ func (c *collection) ensureIndex(ctx context.Context, options indexData) (Index,
|
|||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return nil, false, WithStack(err)
|
||||
}
|
||||
idx, err := newIndex(data.ID, c)
|
||||
idx, err := newIndex(data.ID, data.Type, c)
|
||||
if err != nil {
|
||||
return nil, false, WithStack(err)
|
||||
}
|
||||
|
|
|
@ -79,6 +79,10 @@ type Request interface {
|
|||
Written() bool
|
||||
// Clone creates a new request containing the same data as this request
|
||||
Clone() Request
|
||||
// Path returns the Request path
|
||||
Path() string
|
||||
// Method returns the Request method
|
||||
Method() string
|
||||
}
|
||||
|
||||
// Response represents the response from the server on a given request.
|
||||
|
|
42
deps/github.com/arangodb/go-driver/context.go
vendored
42
deps/github.com/arangodb/go-driver/context.go
vendored
|
@ -57,6 +57,7 @@ const (
|
|||
keyDBServerID ContextKey = "arangodb-dbserverID"
|
||||
keyBatchID ContextKey = "arangodb-batchID"
|
||||
keyJobIDResponse ContextKey = "arangodb-jobIDResponse"
|
||||
keyAllowDirtyReads ContextKey = "arangodb-allowDirtyReads"
|
||||
)
|
||||
|
||||
// WithRevision is used to configure a context to make document
|
||||
|
@ -137,6 +138,14 @@ func WithWaitForSync(parent context.Context, value ...bool) context.Context {
|
|||
return context.WithValue(contextOrBackground(parent), keyWaitForSync, v)
|
||||
}
|
||||
|
||||
// WithAllowDirtyReads is used in an active failover deployment to allow reads from the follower.
|
||||
// You can pass a reference to a boolean that will set according to wether a potentially dirty read
|
||||
// happened or not. nil is allowed.
|
||||
// This is valid for document reads, aql queries, gharial vertex and edge reads.
|
||||
func WithAllowDirtyReads(parent context.Context, wasDirtyRead *bool) context.Context {
|
||||
return context.WithValue(contextOrBackground(parent), keyAllowDirtyReads, wasDirtyRead)
|
||||
}
|
||||
|
||||
// WithRawResponse is used to configure a context that will make all functions store the raw response into a
|
||||
// buffer.
|
||||
func WithRawResponse(parent context.Context, value *[]byte) context.Context {
|
||||
|
@ -231,6 +240,8 @@ type contextSettings struct {
|
|||
ImportDetails *[]string
|
||||
IsRestore bool
|
||||
IsSystem bool
|
||||
AllowDirtyReads bool
|
||||
DirtyReadFlag *bool
|
||||
IgnoreRevs *bool
|
||||
EnforceReplicationFactor *bool
|
||||
Configured *bool
|
||||
|
@ -240,6 +251,29 @@ type contextSettings struct {
|
|||
JobIDResponse *string
|
||||
}
|
||||
|
||||
// loadContextResponseValue loads generic values from the response and puts it into variables specified
|
||||
// via context values.
|
||||
func loadContextResponseValues(cs contextSettings, resp Response) {
|
||||
// Parse potential dirty read
|
||||
if cs.DirtyReadFlag != nil {
|
||||
if dirtyRead := resp.Header("X-Arango-Potential-Dirty-Read"); dirtyRead != "" {
|
||||
*cs.DirtyReadFlag = true // The documentation does not say anything about the actual value (dirtyRead == "true")
|
||||
} else {
|
||||
*cs.DirtyReadFlag = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setDirtyReadFlagIfRequired is a helper function that sets the bool reference for allowDirtyReads to the
|
||||
// specified value, if required and reference is not nil.
|
||||
func setDirtyReadFlagIfRequired(ctx context.Context, wasDirty bool) {
|
||||
if v := ctx.Value(keyAllowDirtyReads); v != nil {
|
||||
if ref, ok := v.(*bool); ok && ref != nil {
|
||||
*ref = wasDirty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applyContextSettings returns the settings configured in the context in the given request.
|
||||
// It then returns information about the applied settings that may be needed later in API implementation functions.
|
||||
func applyContextSettings(ctx context.Context, req Request) contextSettings {
|
||||
|
@ -279,6 +313,14 @@ func applyContextSettings(ctx context.Context, req Request) contextSettings {
|
|||
result.WaitForSync = waitForSync
|
||||
}
|
||||
}
|
||||
// AllowDirtyReads
|
||||
if v := ctx.Value(keyAllowDirtyReads); v != nil {
|
||||
req.SetHeader("x-arango-allow-dirty-read", "true")
|
||||
result.AllowDirtyReads = true
|
||||
if dirtyReadFlag, ok := v.(*bool); ok {
|
||||
result.DirtyReadFlag = dirtyReadFlag
|
||||
}
|
||||
}
|
||||
// ReturnOld
|
||||
if v := ctx.Value(keyReturnOld); v != nil {
|
||||
req.SetQuery("returnOld", "true")
|
||||
|
|
|
@ -33,26 +33,29 @@ import (
|
|||
)
|
||||
|
||||
// newCursor creates a new Cursor implementation.
|
||||
func newCursor(data cursorData, endpoint string, db *database) (Cursor, error) {
|
||||
func newCursor(data cursorData, endpoint string, db *database, allowDirtyReads bool) (Cursor, error) {
|
||||
if db == nil {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "db is nil"})
|
||||
}
|
||||
return &cursor{
|
||||
cursorData: data,
|
||||
endpoint: endpoint,
|
||||
db: db,
|
||||
conn: db.conn,
|
||||
cursorData: data,
|
||||
endpoint: endpoint,
|
||||
db: db,
|
||||
conn: db.conn,
|
||||
allowDirtyReads: allowDirtyReads,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type cursor struct {
|
||||
cursorData
|
||||
endpoint string
|
||||
resultIndex int
|
||||
db *database
|
||||
conn Connection
|
||||
closed int32
|
||||
closeMutex sync.Mutex
|
||||
endpoint string
|
||||
resultIndex int
|
||||
db *database
|
||||
conn Connection
|
||||
closed int32
|
||||
closeMutex sync.Mutex
|
||||
allowDirtyReads bool
|
||||
lastReadWasDirty bool
|
||||
}
|
||||
|
||||
type cursorStats struct {
|
||||
|
@ -140,24 +143,40 @@ func (c *cursor) ReadDocument(ctx context.Context, result interface{}) (Document
|
|||
ctx = WithEndpoint(ctx, c.endpoint)
|
||||
|
||||
if c.resultIndex >= len(c.Result) && c.cursorData.HasMore {
|
||||
// This is required since we are interested if this was a dirty read
|
||||
// but we do not want to trash the users bool reference.
|
||||
var wasDirtyRead bool
|
||||
fetchctx := ctx
|
||||
if c.allowDirtyReads {
|
||||
fetchctx = WithAllowDirtyReads(ctx, &wasDirtyRead)
|
||||
}
|
||||
|
||||
// Fetch next batch
|
||||
req, err := c.conn.NewRequest("PUT", path.Join(c.relPath(), c.cursorData.ID))
|
||||
if err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
resp, err := c.conn.Do(ctx, req)
|
||||
cs := applyContextSettings(fetchctx, req)
|
||||
resp, err := c.conn.Do(fetchctx, req)
|
||||
if err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
loadContextResponseValues(cs, resp)
|
||||
var data cursorData
|
||||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return DocumentMeta{}, WithStack(err)
|
||||
}
|
||||
c.cursorData = data
|
||||
c.resultIndex = 0
|
||||
c.lastReadWasDirty = wasDirtyRead
|
||||
}
|
||||
// ReadDocument should act as if it would actually do a read
|
||||
// hence update the bool reference
|
||||
if c.allowDirtyReads {
|
||||
setDirtyReadFlagIfRequired(ctx, c.lastReadWasDirty)
|
||||
}
|
||||
|
||||
index := c.resultIndex
|
||||
|
|
|
@ -46,6 +46,9 @@ type Database interface {
|
|||
// Collection functions
|
||||
DatabaseCollections
|
||||
|
||||
// View functions
|
||||
DatabaseViews
|
||||
|
||||
// Graph functions
|
||||
DatabaseGraphs
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ func (d *database) Query(ctx context.Context, query string, bindVars map[string]
|
|||
if _, err := req.SetBody(input); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
cs := applyContextSettings(ctx, req)
|
||||
resp, err := d.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
|
@ -150,7 +151,7 @@ func (d *database) Query(ctx context.Context, query string, bindVars map[string]
|
|||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
col, err := newCursor(data, resp.Endpoint(), d)
|
||||
col, err := newCursor(data, resp.Endpoint(), d, cs.AllowDirtyReads)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
|
|
52
deps/github.com/arangodb/go-driver/database_views.go
vendored
Normal file
52
deps/github.com/arangodb/go-driver/database_views.go
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import "context"
|
||||
|
||||
// DatabaseViews provides access to all views in a single database.
|
||||
// Views are only available in ArangoDB 3.4 and higher.
|
||||
type DatabaseViews interface {
|
||||
// View opens a connection to an existing view within the database.
|
||||
// If no collection with given name exists, an NotFoundError is returned.
|
||||
View(ctx context.Context, name string) (View, error)
|
||||
|
||||
// ViewExists returns true if a view with given name exists within the database.
|
||||
ViewExists(ctx context.Context, name string) (bool, error)
|
||||
|
||||
// Views returns a list of all views in the database.
|
||||
Views(ctx context.Context) ([]View, error)
|
||||
|
||||
// CreateArangoSearchView creates a new view of type ArangoSearch,
|
||||
// with given name and options, and opens a connection to it.
|
||||
// If a view with given name already exists within the database, a ConflictError is returned.
|
||||
CreateArangoSearchView(ctx context.Context, name string, options *ArangoSearchViewProperties) (ArangoSearchView, error)
|
||||
}
|
||||
|
||||
// ViewType is the type of a view.
|
||||
type ViewType string
|
||||
|
||||
const (
|
||||
// ViewTypeArangoSearch specifies an ArangoSearch view type.
|
||||
ViewTypeArangoSearch = ViewType("arangosearch")
|
||||
)
|
157
deps/github.com/arangodb/go-driver/database_views_impl.go
vendored
Normal file
157
deps/github.com/arangodb/go-driver/database_views_impl.go
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
)
|
||||
|
||||
type viewInfo struct {
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Type ViewType `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type getViewResponse struct {
|
||||
Result []viewInfo `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// View opens a connection to an existing view within the database.
|
||||
// If no collection with given name exists, an NotFoundError is returned.
|
||||
func (d *database) View(ctx context.Context, name string) (View, error) {
|
||||
escapedName := pathEscape(name)
|
||||
req, err := d.conn.NewRequest("GET", path.Join(d.relPath(), "_api/view", escapedName))
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := d.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
var data viewInfo
|
||||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
view, err := newView(name, data.Type, d)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
return view, nil
|
||||
}
|
||||
|
||||
// ViewExists returns true if a view with given name exists within the database.
|
||||
func (d *database) ViewExists(ctx context.Context, name string) (bool, error) {
|
||||
escapedName := pathEscape(name)
|
||||
req, err := d.conn.NewRequest("GET", path.Join(d.relPath(), "_api/view", escapedName))
|
||||
if err != nil {
|
||||
return false, WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := d.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return false, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err == nil {
|
||||
return true, nil
|
||||
} else if IsNotFound(err) {
|
||||
return false, nil
|
||||
} else {
|
||||
return false, WithStack(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Views returns a list of all views in the database.
|
||||
func (d *database) Views(ctx context.Context) ([]View, error) {
|
||||
req, err := d.conn.NewRequest("GET", path.Join(d.relPath(), "_api/view"))
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := d.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
var data getViewResponse
|
||||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
result := make([]View, 0, len(data.Result))
|
||||
for _, info := range data.Result {
|
||||
view, err := newView(info.Name, info.Type, d)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
result = append(result, view)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// CreateArangoSearchView creates a new view of type ArangoSearch,
|
||||
// with given name and options, and opens a connection to it.
|
||||
// If a view with given name already exists within the database, a ConflictError is returned.
|
||||
func (d *database) CreateArangoSearchView(ctx context.Context, name string, options *ArangoSearchViewProperties) (ArangoSearchView, error) {
|
||||
input := struct {
|
||||
Name string `json:"name"`
|
||||
Type ViewType `json:"type"`
|
||||
ArangoSearchViewProperties // `json:"properties"`
|
||||
}{
|
||||
Name: name,
|
||||
Type: ViewTypeArangoSearch,
|
||||
}
|
||||
if options != nil {
|
||||
input.ArangoSearchViewProperties = *options
|
||||
}
|
||||
req, err := d.conn.NewRequest("POST", path.Join(d.relPath(), "_api/view"))
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
if _, err := req.SetBody(input); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := d.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(201); err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
view, err := newView(name, input.Type, d)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
result, err := view.ArangoSearchView()
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -67,6 +67,9 @@ func (c *edgeCollection) readDocument(ctx context.Context, key string, result in
|
|||
if err := resp.CheckStatus(200); err != nil {
|
||||
return DocumentMeta{}, contextSettings{}, WithStack(err)
|
||||
}
|
||||
// Concerns: ReadDocuments reads multiple documents via multiple calls to readDocument (this function).
|
||||
// Currently with AllowDirtyReads the wasDirtyFlag is only set according to the last read request.
|
||||
loadContextResponseValues(cs, resp)
|
||||
// Parse metadata
|
||||
var meta DocumentMeta
|
||||
if err := resp.ParseBody("edge", &meta); err != nil {
|
||||
|
|
5
deps/github.com/arangodb/go-driver/error.go
vendored
5
deps/github.com/arangodb/go-driver/error.go
vendored
|
@ -128,6 +128,11 @@ func IsNoLeader(err error) bool {
|
|||
return IsArangoErrorWithCode(err, 503) && IsArangoErrorWithErrorNum(err, 1496)
|
||||
}
|
||||
|
||||
// IsNoLeaderOrOngoing return true if the given error is an ArangoError with code 503 and error number 1496 or 1495
|
||||
func IsNoLeaderOrOngoing(err error) bool {
|
||||
return IsArangoErrorWithCode(err, 503) && (IsArangoErrorWithErrorNum(err, 1495) || IsArangoErrorWithErrorNum(err, 1496))
|
||||
}
|
||||
|
||||
// InvalidArgumentError is returned when a go function argument is invalid.
|
||||
type InvalidArgumentError struct {
|
||||
Message string
|
||||
|
|
120
deps/github.com/arangodb/go-driver/example_graph_test.go
vendored
Normal file
120
deps/github.com/arangodb/go-driver/example_graph_test.go
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
|
||||
// +build !auth
|
||||
|
||||
// This example demonstrates how to create a graph, how to add vertices and edges and how to delete it again.
|
||||
package driver_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
|
||||
driver "github.com/arangodb/go-driver"
|
||||
"github.com/arangodb/go-driver/http"
|
||||
)
|
||||
|
||||
type MyObject struct {
|
||||
Name string `json:"_key"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
type MyEdgeObject struct {
|
||||
From string `json:"_from"`
|
||||
To string `json:"_to"`
|
||||
}
|
||||
|
||||
func Example_createGraph() {
|
||||
fmt.Println("Hello World")
|
||||
|
||||
// Create an HTTP connection to the database
|
||||
conn, err := http.NewConnection(http.ConnectionConfig{
|
||||
Endpoints: []string{"http://localhost:8529"},
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create HTTP connection: %v", err)
|
||||
}
|
||||
// Create a client
|
||||
c, err := driver.NewClient(driver.ClientConfig{
|
||||
Connection: conn,
|
||||
})
|
||||
|
||||
// Create database
|
||||
db, err := c.CreateDatabase(nil, "my_graph_db", nil)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create database: %v", err)
|
||||
}
|
||||
|
||||
// define the edgeCollection to store the edges
|
||||
var edgeDefinition driver.EdgeDefinition
|
||||
edgeDefinition.Collection = "myEdgeCollection"
|
||||
// define a set of collections where an edge is going out...
|
||||
edgeDefinition.From = []string{"myCollection1", "myCollection2"}
|
||||
|
||||
// repeat this for the collections where an edge is going into
|
||||
edgeDefinition.To = []string{"myCollection1", "myCollection3"}
|
||||
|
||||
// A graph can contain additional vertex collections, defined in the set of orphan collections
|
||||
var options driver.CreateGraphOptions
|
||||
options.OrphanVertexCollections = []string{"myCollection4", "myCollection5"}
|
||||
options.EdgeDefinitions = []driver.EdgeDefinition{edgeDefinition}
|
||||
|
||||
// now it's possible to create a graph
|
||||
graph, err := db.CreateGraph(nil, "myGraph", &options)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create graph: %v", err)
|
||||
}
|
||||
|
||||
// add vertex
|
||||
vertexCollection1, err := graph.VertexCollection(nil, "myCollection1")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get vertex collection: %v", err)
|
||||
}
|
||||
|
||||
myObjects := []MyObject{
|
||||
MyObject{
|
||||
"Homer",
|
||||
38,
|
||||
},
|
||||
MyObject{
|
||||
"Marge",
|
||||
36,
|
||||
},
|
||||
}
|
||||
_, _, err = vertexCollection1.CreateDocuments(nil, myObjects)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create vertex documents: %v", err)
|
||||
}
|
||||
|
||||
// add edge
|
||||
edgeCollection, _, err := graph.EdgeCollection(nil, "myEdgeCollection")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to select edge collection: %v", err)
|
||||
}
|
||||
|
||||
edge := MyEdgeObject{From: "myCollection1/Homer", To: "myCollection1/Marge"}
|
||||
_, err = edgeCollection.CreateDocument(nil, edge)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create edge document: %v", err)
|
||||
}
|
||||
|
||||
// delete graph
|
||||
graph.Remove(nil)
|
||||
}
|
7
deps/github.com/arangodb/go-driver/go.mod
vendored
Normal file
7
deps/github.com/arangodb/go-driver/go.mod
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
module github.com/arangodb/go-driver
|
||||
|
||||
require (
|
||||
github.com/arangodb/go-velocypack v0.0.0-20180928134037-d177e3455691
|
||||
github.com/coreos/go-iptables v0.4.0
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
)
|
12
deps/github.com/arangodb/go-driver/go.sum
vendored
Normal file
12
deps/github.com/arangodb/go-driver/go.sum
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
github.com/arangodb/go-velocypack v0.0.0-20180928134037-d177e3455691 h1:62SGGAvrKTXOrewra74du0H98Yg/eufQ/tO3RoIP8Bs=
|
||||
github.com/arangodb/go-velocypack v0.0.0-20180928134037-d177e3455691/go.mod h1:7QCjpWXdB49P6fql1CxmsBWd8z/T4L4pqFLTnc10xNM=
|
||||
github.com/coreos/go-iptables v0.4.0 h1:wh4UbVs8DhLUbpyq97GLJDKrQMjEDD63T1xE4CrsKzQ=
|
||||
github.com/coreos/go-iptables v0.4.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
@ -47,6 +47,16 @@ type httpJSONRequest struct {
|
|||
written bool
|
||||
}
|
||||
|
||||
// Path returns the Request path
|
||||
func (r *httpJSONRequest) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
// Method returns the Request method
|
||||
func (r *httpJSONRequest) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
// Clone creates a new request containing the same data as this request
|
||||
func (r *httpJSONRequest) Clone() driver.Request {
|
||||
clone := *r
|
||||
|
|
|
@ -47,6 +47,16 @@ type httpVPackRequest struct {
|
|||
written bool
|
||||
}
|
||||
|
||||
// Path returns the Request path
|
||||
func (r *httpVPackRequest) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
// Method returns the Request method
|
||||
func (r *httpVPackRequest) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
// Clone creates a new request containing the same data as this request
|
||||
func (r *httpVPackRequest) Clone() driver.Request {
|
||||
clone := *r
|
||||
|
|
16
deps/github.com/arangodb/go-driver/index.go
vendored
16
deps/github.com/arangodb/go-driver/index.go
vendored
|
@ -24,11 +24,27 @@ package driver
|
|||
|
||||
import "context"
|
||||
|
||||
// IndexType represents a index type as string
|
||||
type IndexType string
|
||||
|
||||
// Symbolic constants for index types
|
||||
const (
|
||||
PrimaryIndex = IndexType("primary")
|
||||
FullTextIndex = IndexType("fulltext")
|
||||
HashIndex = IndexType("hash")
|
||||
SkipListIndex = IndexType("skiplist")
|
||||
PersistentIndex = IndexType("persistent")
|
||||
GeoIndex = IndexType("geo")
|
||||
)
|
||||
|
||||
// Index provides access to a single index in a single collection.
|
||||
type Index interface {
|
||||
// Name returns the name of the index.
|
||||
Name() string
|
||||
|
||||
// Type returns the type of the index
|
||||
Type() IndexType
|
||||
|
||||
// Remove removes the entire index.
|
||||
// If the index does not exist, a NotFoundError is returned.
|
||||
Remove(ctx context.Context) error
|
||||
|
|
50
deps/github.com/arangodb/go-driver/index_impl.go
vendored
50
deps/github.com/arangodb/go-driver/index_impl.go
vendored
|
@ -28,8 +28,29 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// indexStringToType converts a string representation of an index to IndexType
|
||||
func indexStringToType(indexTypeString string) (IndexType, error) {
|
||||
switch indexTypeString {
|
||||
case string(FullTextIndex):
|
||||
return FullTextIndex, nil
|
||||
case string(HashIndex):
|
||||
return HashIndex, nil
|
||||
case string(SkipListIndex):
|
||||
return SkipListIndex, nil
|
||||
case string(PrimaryIndex):
|
||||
return PrimaryIndex, nil
|
||||
case string(PersistentIndex):
|
||||
return PersistentIndex, nil
|
||||
case string(GeoIndex), "geo1", "geo2":
|
||||
return GeoIndex, nil
|
||||
|
||||
default:
|
||||
return "", WithStack(InvalidArgumentError{Message: "unknown index type"})
|
||||
}
|
||||
}
|
||||
|
||||
// newIndex creates a new Index implementation.
|
||||
func newIndex(id string, col *collection) (Index, error) {
|
||||
func newIndex(id string, indexTypeString string, col *collection) (Index, error) {
|
||||
if id == "" {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "id is empty"})
|
||||
}
|
||||
|
@ -40,19 +61,25 @@ func newIndex(id string, col *collection) (Index, error) {
|
|||
if col == nil {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "col is nil"})
|
||||
}
|
||||
indexType, err := indexStringToType(indexTypeString)
|
||||
if err != nil {
|
||||
return nil, WithStack(err)
|
||||
}
|
||||
return &index{
|
||||
id: id,
|
||||
col: col,
|
||||
db: col.db,
|
||||
conn: col.conn,
|
||||
id: id,
|
||||
indexType: indexType,
|
||||
col: col,
|
||||
db: col.db,
|
||||
conn: col.conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type index struct {
|
||||
id string
|
||||
db *database
|
||||
col *collection
|
||||
conn Connection
|
||||
id string
|
||||
indexType IndexType
|
||||
db *database
|
||||
col *collection
|
||||
conn Connection
|
||||
}
|
||||
|
||||
// relPath creates the relative path to this index (`_db/<db-name>/_api/index`)
|
||||
|
@ -66,6 +93,11 @@ func (i *index) Name() string {
|
|||
return parts[1]
|
||||
}
|
||||
|
||||
// Type returns the type of the index
|
||||
func (i *index) Type() IndexType {
|
||||
return i.indexType
|
||||
}
|
||||
|
||||
// Remove removes the entire index.
|
||||
// If the index does not exist, a NotFoundError is returned.
|
||||
func (i *index) Remove(ctx context.Context) error {
|
||||
|
|
|
@ -25,6 +25,7 @@ package test
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
httplib "net/http"
|
||||
"os"
|
||||
|
@ -178,14 +179,14 @@ func createClientFromEnv(t testEnv, waitUntilReady bool, connection ...*driver.C
|
|||
t.Fatalf("Failed to create new client: %s", describe(err))
|
||||
}
|
||||
if waitUntilReady {
|
||||
timeout := 3 * time.Minute
|
||||
timeout := time.Minute
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
if up := waitUntilServerAvailable(ctx, c, t); !up {
|
||||
t.Fatalf("Connection is not available in %s", timeout)
|
||||
if up := waitUntilServerAvailable(ctx, c, t); up != nil {
|
||||
t.Fatalf("Connection is not available in %s: %s", timeout, describe(up))
|
||||
}
|
||||
// Synchronize endpoints
|
||||
if err := c.SynchronizeEndpoints(context.Background()); err != nil {
|
||||
if err := waitUntilEndpointSynchronized(ctx, c, "", t); err != nil {
|
||||
t.Errorf("Failed to synchronize endpoints: %s", describe(err))
|
||||
} else {
|
||||
logEndpointsOnce.Do(func() {
|
||||
|
@ -197,27 +198,68 @@ func createClientFromEnv(t testEnv, waitUntilReady bool, connection ...*driver.C
|
|||
}
|
||||
|
||||
// waitUntilServerAvailable keeps waiting until the server/cluster that the client is addressing is available.
|
||||
func waitUntilServerAvailable(ctx context.Context, c driver.Client, t testEnv) bool {
|
||||
instanceUp := make(chan bool)
|
||||
func waitUntilServerAvailable(ctx context.Context, c driver.Client, t testEnv) error {
|
||||
instanceUp := make(chan error)
|
||||
go func() {
|
||||
for {
|
||||
verCtx, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||
if _, err := c.Version(verCtx); err == nil {
|
||||
|
||||
// check if leader challenge is ongoing
|
||||
if _, err := c.ServerRole(verCtx); err != nil {
|
||||
if !driver.IsNoLeaderOrOngoing(err) {
|
||||
cancel()
|
||||
instanceUp <- err
|
||||
return
|
||||
}
|
||||
//t.Logf("Retry. Waiting for leader: %s", describe(err))
|
||||
continue
|
||||
}
|
||||
|
||||
//t.Logf("Found version %s", v.Version)
|
||||
cancel()
|
||||
instanceUp <- true
|
||||
instanceUp <- nil
|
||||
return
|
||||
}
|
||||
cancel()
|
||||
//t.Logf("Version failed: %s %#v", describe(err), err)
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case up := <-instanceUp:
|
||||
return up
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// waitUntilEndpointSynchronized keeps waiting until the endpoints are synchronized. leadership might be ongoing.
|
||||
func waitUntilEndpointSynchronized(ctx context.Context, c driver.Client, dbname string, t testEnv) error {
|
||||
endpointsSynced := make(chan error)
|
||||
go func() {
|
||||
for {
|
||||
callCtx, cancel := context.WithTimeout(ctx, time.Second*5)
|
||||
if err := c.SynchronizeEndpoints2(callCtx, dbname); err != nil {
|
||||
t.Logf("SynchonizedEnpoints failed: %s", describe(err))
|
||||
} else {
|
||||
cancel()
|
||||
endpointsSynced <- nil
|
||||
return
|
||||
}
|
||||
cancel()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-time.After(time.Second):
|
||||
}
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case up := <-endpointsSynced:
|
||||
return up
|
||||
case <-ctx.Done():
|
||||
return fmt.Errorf("Timeout while synchronizing endpoints")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,8 +303,8 @@ func TestCreateClientHttpConnectionCustomTransport(t *testing.T) {
|
|||
timeout := 3 * time.Minute
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
if up := waitUntilServerAvailable(ctx, c, t); !up {
|
||||
t.Fatalf("Connection is not available in %s", timeout)
|
||||
if up := waitUntilServerAvailable(ctx, c, t); up != nil {
|
||||
t.Fatalf("Connection is not available in %s: %s", timeout, describe(up))
|
||||
}
|
||||
if info, err := c.Version(driver.WithDetails(ctx)); err != nil {
|
||||
t.Errorf("Version failed: %s", describe(err))
|
||||
|
|
|
@ -50,5 +50,5 @@ if [ "$CMD" == "start" ]; then
|
|||
${STARTER} \
|
||||
--starter.port=7000 --starter.address=127.0.0.1 \
|
||||
--docker.image=${ARANGODB} \
|
||||
--starter.local --starter.mode=${STARTERMODE} --all.log.output=+ $STARTERARGS
|
||||
--starter.local --starter.mode=${STARTERMODE} --all.log.output=+ $STARTERARGS
|
||||
fi
|
||||
|
|
|
@ -51,6 +51,15 @@ func TestClusterHealth(t *testing.T) {
|
|||
dbservers := 0
|
||||
coordinators := 0
|
||||
for _, sh := range h.Health {
|
||||
|
||||
if v, err := c.Version(nil); err == nil {
|
||||
if v.Version.CompareTo(sh.Version) != 0 {
|
||||
t.Logf("Server version differs from `_api/version`, got `%s` and `%s`", v.Version, sh.Version)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Version failed: %s", describe(err))
|
||||
}
|
||||
|
||||
switch sh.Role {
|
||||
case driver.ServerRoleAgent:
|
||||
agents++
|
||||
|
@ -205,3 +214,110 @@ func TestClusterMoveShard(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestClusterMoveShardWithViews tests the Cluster.MoveShard method with collection
|
||||
// that are being used in views.
|
||||
func TestClusterMoveShardWithViews(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
cl, err := c.Cluster(ctx)
|
||||
if driver.IsPreconditionFailed(err) {
|
||||
t.Skip("Not a cluster")
|
||||
} else {
|
||||
db, err := c.Database(ctx, "_system")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open _system database: %s", describe(err))
|
||||
}
|
||||
col, err := db.CreateCollection(ctx, "test_move_shard_with_view", &driver.CreateCollectionOptions{
|
||||
NumberOfShards: 12,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateCollection failed: %s", describe(err))
|
||||
}
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"test_move_shard_with_view": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
viewName := "test_move_shard_view"
|
||||
if _, err := db.CreateArangoSearchView(ctx, viewName, opts); err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", viewName, describe(err))
|
||||
}
|
||||
h, err := cl.Health(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Health failed: %s", describe(err))
|
||||
}
|
||||
inv, err := cl.DatabaseInventory(ctx, db)
|
||||
if err != nil {
|
||||
t.Fatalf("DatabaseInventory failed: %s", describe(err))
|
||||
}
|
||||
if len(inv.Collections) == 0 {
|
||||
t.Error("Expected multiple collections, got 0")
|
||||
}
|
||||
var targetServerID driver.ServerID
|
||||
for id, s := range h.Health {
|
||||
if s.Role == driver.ServerRoleDBServer {
|
||||
targetServerID = id
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(targetServerID) == 0 {
|
||||
t.Fatalf("Failed to find any dbserver")
|
||||
}
|
||||
movedShards := 0
|
||||
for _, colInv := range inv.Collections {
|
||||
if colInv.Parameters.Name == col.Name() {
|
||||
for shardID, dbServers := range colInv.Parameters.Shards {
|
||||
if dbServers[0] != targetServerID {
|
||||
movedShards++
|
||||
var rawResponse []byte
|
||||
if err := cl.MoveShard(driver.WithRawResponse(ctx, &rawResponse), col, shardID, dbServers[0], targetServerID); err != nil {
|
||||
t.Errorf("MoveShard for shard %s in collection %s failed: %s (raw response '%s' %x)", shardID, col.Name(), describe(err), string(rawResponse), rawResponse)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if movedShards == 0 {
|
||||
t.Fatal("Expected to have moved at least 1 shard, all seem to be on target server already")
|
||||
}
|
||||
// Wait until all shards are on the targetServerID
|
||||
start := time.Now()
|
||||
maxTestTime := time.Minute
|
||||
lastShardsNotOnTargetServerID := movedShards
|
||||
for {
|
||||
shardsNotOnTargetServerID := 0
|
||||
inv, err := cl.DatabaseInventory(ctx, db)
|
||||
if err != nil {
|
||||
t.Errorf("DatabaseInventory failed: %s", describe(err))
|
||||
} else {
|
||||
for _, colInv := range inv.Collections {
|
||||
if colInv.Parameters.Name == col.Name() {
|
||||
for shardID, dbServers := range colInv.Parameters.Shards {
|
||||
if dbServers[0] != targetServerID {
|
||||
shardsNotOnTargetServerID++
|
||||
t.Logf("Shard %s in on %s, wanted %s", shardID, dbServers[0], targetServerID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if shardsNotOnTargetServerID == 0 {
|
||||
// We're done
|
||||
break
|
||||
}
|
||||
if shardsNotOnTargetServerID != lastShardsNotOnTargetServerID {
|
||||
// Something changed, we give a bit more time
|
||||
maxTestTime = maxTestTime + time.Second*15
|
||||
lastShardsNotOnTargetServerID = shardsNotOnTargetServerID
|
||||
}
|
||||
if time.Since(start) > maxTestTime {
|
||||
t.Errorf("%d shards did not move within %s", shardsNotOnTargetServerID, maxTestTime)
|
||||
break
|
||||
}
|
||||
t.Log("Waiting a bit")
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
68
deps/github.com/arangodb/go-driver/test/document_read_test.go
vendored
Normal file
68
deps/github.com/arangodb/go-driver/test/document_read_test.go
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2017 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Lars Maier
|
||||
//
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
driver "github.com/arangodb/go-driver"
|
||||
)
|
||||
|
||||
// TestReadDocumentWithIfMatch creates a document and reads it with a non-matching revision.
|
||||
func TestReadDocumentWithIfMatch(t *testing.T) {
|
||||
c := createClientFromEnv(t, true)
|
||||
db := ensureDatabase(nil, c, "document_read_test", nil, t)
|
||||
col := ensureCollection(nil, db, "document_read_test", nil, t)
|
||||
doc := UserDoc{
|
||||
"Jan",
|
||||
40,
|
||||
}
|
||||
meta, err := col.CreateDocument(nil, doc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create new document: %s", describe(err))
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx = driver.WithRevision(ctx, meta.Rev)
|
||||
|
||||
meta2, err := col.ReadDocument(ctx, meta.Key, &doc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read document: %s", describe(err))
|
||||
}
|
||||
if meta2 != meta {
|
||||
t.Error("Read wrong meta data.")
|
||||
}
|
||||
|
||||
var resp driver.Response
|
||||
ctx2 := context.Background()
|
||||
ctx2 = driver.WithRevision(ctx2, "nonsense")
|
||||
ctx2 = driver.WithResponse(ctx2, &resp)
|
||||
_, err = col.ReadDocument(ctx2, meta.Key, &doc)
|
||||
if err == nil {
|
||||
t.Error("Reading with wrong revision did not fail")
|
||||
}
|
||||
if resp.StatusCode() != 412 {
|
||||
t.Errorf("Expected status code 412, found %d", resp.StatusCode())
|
||||
}
|
||||
}
|
|
@ -50,6 +50,9 @@ func TestEnsureFullTextIndex(t *testing.T) {
|
|||
if !created {
|
||||
t.Error("Expected created to be true, got false")
|
||||
}
|
||||
if idxType := idx.Type(); idxType != driver.FullTextIndex {
|
||||
t.Errorf("Expected FullTextIndex, found `%s`", idxType)
|
||||
}
|
||||
|
||||
// Index must exists now
|
||||
if found, err := col.IndexExists(nil, idx.Name()); err != nil {
|
||||
|
@ -102,6 +105,9 @@ func TestEnsureGeoIndex(t *testing.T) {
|
|||
if !created {
|
||||
t.Error("Expected created to be true, got false")
|
||||
}
|
||||
if idxType := idx.Type(); idxType != driver.GeoIndex {
|
||||
t.Errorf("Expected GeoIndex, found `%s`", idxType)
|
||||
}
|
||||
|
||||
// Index must exists now
|
||||
if found, err := col.IndexExists(nil, idx.Name()); err != nil {
|
||||
|
@ -156,6 +162,9 @@ func TestEnsureHashIndex(t *testing.T) {
|
|||
if !created {
|
||||
t.Error("Expected created to be true, got false")
|
||||
}
|
||||
if idxType := idx.Type(); idxType != driver.HashIndex {
|
||||
t.Errorf("Expected HashIndex, found `%s`", idxType)
|
||||
}
|
||||
|
||||
// Index must exists now
|
||||
if found, err := col.IndexExists(nil, idx.Name()); err != nil {
|
||||
|
@ -210,6 +219,9 @@ func TestEnsurePersistentIndex(t *testing.T) {
|
|||
if !created {
|
||||
t.Error("Expected created to be true, got false")
|
||||
}
|
||||
if idxType := idx.Type(); idxType != driver.PersistentIndex {
|
||||
t.Errorf("Expected PersistentIndex, found `%s`", idxType)
|
||||
}
|
||||
|
||||
// Index must exists now
|
||||
if found, err := col.IndexExists(nil, idx.Name()); err != nil {
|
||||
|
@ -264,6 +276,9 @@ func TestEnsureSkipListIndex(t *testing.T) {
|
|||
if !created {
|
||||
t.Error("Expected created to be true, got false")
|
||||
}
|
||||
if idxType := idx.Type(); idxType != driver.SkipListIndex {
|
||||
t.Errorf("Expected SkipListIndex, found `%s`", idxType)
|
||||
}
|
||||
|
||||
// Index must exists now
|
||||
if found, err := col.IndexExists(nil, idx.Name()); err != nil {
|
||||
|
|
|
@ -29,6 +29,23 @@ import (
|
|||
driver "github.com/arangodb/go-driver"
|
||||
)
|
||||
|
||||
// TestDefaultIndexes creates a collection without any custom index.
|
||||
func TestDefaultIndexes(t *testing.T) {
|
||||
c := createClientFromEnv(t, true)
|
||||
db := ensureDatabase(nil, c, "index_test", nil, t)
|
||||
col := ensureCollection(nil, db, "def_indexes_test", nil, t)
|
||||
|
||||
// Get list of indexes
|
||||
if idxs, err := col.Indexes(context.Background()); err != nil {
|
||||
t.Fatalf("Failed to get indexes: %s", describe(err))
|
||||
} else {
|
||||
if len(idxs) != 1 {
|
||||
// 1 is always added by the system
|
||||
t.Errorf("Expected 1 index, got %d", len(idxs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateFullTextIndex creates a collection with a full text index.
|
||||
func TestIndexes(t *testing.T) {
|
||||
c := createClientFromEnv(t, true)
|
||||
|
@ -36,10 +53,19 @@ func TestIndexes(t *testing.T) {
|
|||
col := ensureCollection(nil, db, "indexes_test", nil, t)
|
||||
|
||||
// Create some indexes
|
||||
if _, _, err := col.EnsureFullTextIndex(nil, []string{"name"}, nil); err != nil {
|
||||
if idx, _, err := col.EnsureFullTextIndex(nil, []string{"name"}, nil); err == nil {
|
||||
if idxType := idx.Type(); idxType != driver.FullTextIndex {
|
||||
t.Errorf("Expected FullTextIndex, found `%s`", idxType)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Failed to create new index: %s", describe(err))
|
||||
}
|
||||
if _, _, err := col.EnsureHashIndex(nil, []string{"age", "gender"}, nil); err != nil {
|
||||
|
||||
if idx, _, err := col.EnsureHashIndex(nil, []string{"age", "gender"}, nil); err == nil {
|
||||
if idxType := idx.Type(); idxType != driver.HashIndex {
|
||||
t.Errorf("Expected HashIndex, found `%s`", idxType)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Failed to create new index: %s", describe(err))
|
||||
}
|
||||
|
||||
|
|
|
@ -51,6 +51,7 @@ func TestUpdateUserPasswordMyself(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
ensureSynchronizedEndpoints(authClient, "", t)
|
||||
|
||||
if isVST1_0 && !isv32p {
|
||||
t.Skip("Cannot update my own password using VST in 3.1")
|
||||
|
@ -89,6 +90,7 @@ func TestUpdateUserPasswordOtherUser(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
ensureSynchronizedEndpoints(authClient, "", t)
|
||||
|
||||
if isVST1_0 && !isv32p {
|
||||
t.Skip("Cannot update other password using VST in 3.1")
|
||||
|
@ -146,12 +148,10 @@ func TestGrantUserDatabase(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
ensureSynchronizedEndpoints(authClient, "grant_user_test", t)
|
||||
authDb := waitForDatabaseAccess(authClient, "grant_user_test", t)
|
||||
|
||||
// Try to create a collection in the db
|
||||
authDb, err := authClient.Database(nil, "grant_user_test")
|
||||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
if _, err := authDb.CreateCollection(nil, "some_collection", nil); err != nil {
|
||||
t.Errorf("Expected success, got %s", describe(err))
|
||||
}
|
||||
|
@ -230,12 +230,10 @@ func TestGrantUserDefaultDatabase(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
ensureSynchronizedEndpoints(authClient, "grant_user_def_test", t)
|
||||
|
||||
// Try to create a collection in the db, should succeed
|
||||
authDb, err := authClient.Database(nil, db.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
authDb := waitForDatabaseAccess(authClient, "grant_user_def_test", t)
|
||||
|
||||
authCol, err := authDb.CreateCollection(nil, "books_def_db", nil)
|
||||
if err != nil {
|
||||
|
@ -348,11 +346,26 @@ func TestGrantUserCollection(t *testing.T) {
|
|||
if err := u.SetCollectionAccess(nil, col, driver.GrantReadWrite); err != nil {
|
||||
t.Fatalf("SetCollectionAccess failed: %s", describe(err))
|
||||
}
|
||||
// Read back collection access
|
||||
if grant, err := u.GetCollectionAccess(nil, col); err != nil {
|
||||
t.Fatalf("GetCollectionAccess failed: %s", describe(err))
|
||||
} else if grant != driver.GrantReadWrite {
|
||||
t.Errorf("Collection access invalid, expected 'rw', got '%s'", grant)
|
||||
|
||||
// wait for change to propagate
|
||||
{
|
||||
deadline := time.Now().Add(time.Minute)
|
||||
for {
|
||||
// Read back collection access
|
||||
if grant, err := u.GetCollectionAccess(nil, col); err == nil {
|
||||
if grant == driver.GrantReadWrite {
|
||||
break
|
||||
}
|
||||
if time.Now().Before(deadline) {
|
||||
t.Logf("Expected failure, got %s, trying again...", describe(err))
|
||||
time.Sleep(time.Second * 2)
|
||||
continue
|
||||
}
|
||||
t.Errorf("Collection access invalid, expected 'rw', got '%s'", grant)
|
||||
} else {
|
||||
t.Fatalf("GetCollectionAccess failed: %s", describe(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
authClient, err := driver.NewClient(driver.ClientConfig{
|
||||
|
@ -362,12 +375,10 @@ func TestGrantUserCollection(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
ensureSynchronizedEndpoints(authClient, "grant_user_col_test", t)
|
||||
authDb := waitForDatabaseAccess(authClient, "grant_user_col_test", t)
|
||||
|
||||
// Try to create a document in the col
|
||||
authDb, err := authClient.Database(nil, db.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
}
|
||||
authCol, err := authDb.Collection(nil, col.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected success, got %s", describe(err))
|
||||
|
@ -602,3 +613,28 @@ func TestUserAccessibleDatabases(t *testing.T) {
|
|||
t.Logf("Last part of test fails on version < 3.2 (got version %s)", version.Version)
|
||||
}
|
||||
}
|
||||
|
||||
func waitForDatabaseAccess(authClient driver.Client, dbname string, t *testing.T) driver.Database {
|
||||
deadline := time.Now().Add(time.Minute)
|
||||
for {
|
||||
// Try to select the database
|
||||
authDb, err := authClient.Database(nil, dbname)
|
||||
if err == nil {
|
||||
return authDb
|
||||
}
|
||||
if time.Now().Before(deadline) {
|
||||
t.Logf("Expected success, got %s, trying again...", describe(err))
|
||||
time.Sleep(time.Second * 2)
|
||||
continue
|
||||
}
|
||||
t.Fatalf("Failed to select database, got %s", describe(err))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func ensureSynchronizedEndpoints(authClient driver.Client, dbname string, t *testing.T) {
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*10)
|
||||
if err := waitUntilEndpointSynchronized(ctx, authClient, dbname, t); err != nil {
|
||||
t.Fatalf("Failed to synchronize endpoint: %s", describe(err))
|
||||
}
|
||||
}
|
||||
|
|
502
deps/github.com/arangodb/go-driver/test/view_test.go
vendored
Normal file
502
deps/github.com/arangodb/go-driver/test/view_test.go
vendored
Normal file
|
@ -0,0 +1,502 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
driver "github.com/arangodb/go-driver"
|
||||
)
|
||||
|
||||
// ensureArangoSearchView is a helper to check if an arangosearch view exists and create it if needed.
|
||||
// It will fail the test when an error occurs.
|
||||
func ensureArangoSearchView(ctx context.Context, db driver.Database, name string, options *driver.ArangoSearchViewProperties, t testEnv) driver.ArangoSearchView {
|
||||
v, err := db.View(ctx, name)
|
||||
if driver.IsNotFound(err) {
|
||||
v, err = db.CreateArangoSearchView(ctx, name, options)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create arangosearch view '%s': %s", name, describe(err))
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Fatalf("Failed to open view '%s': %s", name, describe(err))
|
||||
}
|
||||
result, err := v.ArangoSearchView()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open view '%s' as arangosearch view: %s", name, describe(err))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// checkLinkExists tests if a given collection is linked to the given arangosearch view
|
||||
func checkLinkExists(ctx context.Context, view driver.ArangoSearchView, colName string, t testEnv) bool {
|
||||
props, err := view.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to get view properties: %s", describe(err))
|
||||
}
|
||||
links := props.Links
|
||||
if _, exists := links[colName]; !exists {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// tryAddArangoSearchLink is a helper that adds a link to a view and collection.
|
||||
// It will fail the test when an error occurs and returns wether the link is actually there or not.
|
||||
func tryAddArangoSearchLink(ctx context.Context, db driver.Database, view driver.ArangoSearchView, colName string, t testEnv) bool {
|
||||
addprop := driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
colName: driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
if err := view.SetProperties(ctx, addprop); err != nil {
|
||||
t.Fatalf("Could not create link, view: %s, collection: %s, error: %s", view.Name(), colName, describe(err))
|
||||
}
|
||||
return checkLinkExists(ctx, view, colName, t)
|
||||
}
|
||||
|
||||
// assertArangoSearchView is a helper to check if an arangosearch view exists and fail if it does not.
|
||||
func assertArangoSearchView(ctx context.Context, db driver.Database, name string, t *testing.T) driver.ArangoSearchView {
|
||||
v, err := db.View(ctx, name)
|
||||
if driver.IsNotFound(err) {
|
||||
t.Fatalf("View '%s': does not exist", name)
|
||||
} else if err != nil {
|
||||
t.Fatalf("Failed to open view '%s': %s", name, describe(err))
|
||||
}
|
||||
result, err := v.ArangoSearchView()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to open view '%s' as arangosearch view: %s", name, describe(err))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// TestCreateArangoSearchView creates an arangosearch view and then checks that it exists.
|
||||
func TestCreateArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
ensureCollection(ctx, db, "someCol", nil, t)
|
||||
name := "test_create_asview"
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"someCol": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
v, err := db.CreateArangoSearchView(ctx, name, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if !found {
|
||||
t.Errorf("ViewExists('%s') return false, expected true", name)
|
||||
}
|
||||
// Check v.Name
|
||||
if actualName := v.Name(); actualName != name {
|
||||
t.Errorf("Name() failed. Got '%s', expected '%s'", actualName, name)
|
||||
}
|
||||
// Check v properties
|
||||
p, err := v.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Properties failed: %s", describe(err))
|
||||
}
|
||||
if len(p.Links) != 1 {
|
||||
t.Errorf("Expected 1 link, got %d", len(p.Links))
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateArangoSearchViewInvalidLinks attempts to create an arangosearch view with invalid links and then checks that it does not exists.
|
||||
func TestCreateArangoSearchViewInvalidLinks(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
name := "test_create_inv_view"
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"some_nonexistent_col": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
_, err := db.CreateArangoSearchView(ctx, name, opts)
|
||||
if err == nil {
|
||||
t.Fatalf("Creating view did not fail")
|
||||
}
|
||||
// View must not exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if found {
|
||||
t.Errorf("ViewExists('%s') return true, expected false", name)
|
||||
}
|
||||
// Try to open view, must fail as well
|
||||
if v, err := db.View(ctx, name); !driver.IsNotFound(err) {
|
||||
t.Errorf("Expected NotFound error from View('%s'), got %s instead (%#v)", name, describe(err), v)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateEmptyArangoSearchView creates an arangosearch view without any links.
|
||||
func TestCreateEmptyArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
name := "test_create_empty_asview"
|
||||
v, err := db.CreateArangoSearchView(ctx, name, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if !found {
|
||||
t.Errorf("ViewExists('%s') return false, expected true", name)
|
||||
}
|
||||
// Check v properties
|
||||
p, err := v.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Properties failed: %s", describe(err))
|
||||
}
|
||||
if len(p.Links) != 0 {
|
||||
t.Errorf("Expected 0 links, got %d", len(p.Links))
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateDuplicateArangoSearchView creates an arangosearch view twice and then checks that it exists.
|
||||
func TestCreateDuplicateArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
name := "test_create_dup_asview"
|
||||
if _, err := db.CreateArangoSearchView(ctx, name, nil); err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if !found {
|
||||
t.Errorf("ViewExists('%s') return false, expected true", name)
|
||||
}
|
||||
// Try to create again. Must fail
|
||||
if _, err := db.CreateArangoSearchView(ctx, name, nil); !driver.IsConflict(err) {
|
||||
t.Fatalf("Expect a Conflict error from CreateArangoSearchView, got %s", describe(err))
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreateArangoSearchViewThenRemoveCollection creates an arangosearch view
|
||||
// with a link to an existing collection and the removes that collection.
|
||||
func TestCreateArangoSearchViewThenRemoveCollection(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
col := ensureCollection(ctx, db, "someViewTmpCol", nil, t)
|
||||
name := "test_create_view_then_rem_col"
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"someViewTmpCol": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
v, err := db.CreateArangoSearchView(ctx, name, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if !found {
|
||||
t.Errorf("ViewExists('%s') return false, expected true", name)
|
||||
}
|
||||
// Check v.Name
|
||||
if actualName := v.Name(); actualName != name {
|
||||
t.Errorf("Name() failed. Got '%s', expected '%s'", actualName, name)
|
||||
}
|
||||
// Check v properties
|
||||
p, err := v.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Properties failed: %s", describe(err))
|
||||
}
|
||||
if len(p.Links) != 1 {
|
||||
t.Errorf("Expected 1 link, got %d", len(p.Links))
|
||||
}
|
||||
|
||||
// Now delete the collection
|
||||
if err := col.Remove(ctx); err != nil {
|
||||
t.Fatalf("Failed to remove collection '%s': %s", col.Name(), describe(err))
|
||||
}
|
||||
|
||||
// Re-check v properties
|
||||
p, err = v.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Properties failed: %s", describe(err))
|
||||
}
|
||||
if len(p.Links) != 0 {
|
||||
// TODO is the really the correct expected behavior.
|
||||
t.Errorf("Expected 0 links, got %d", len(p.Links))
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddCollectionMultipleViews creates a collection and two view. adds the collection to both views
|
||||
// and checks if the links exist. The links are set via modifying properties.
|
||||
func TestAddCollectionMultipleViews(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "col_in_multi_view_test", nil, t)
|
||||
ensureCollection(ctx, db, "col_in_multi_view", nil, t)
|
||||
v1 := ensureArangoSearchView(ctx, db, "col_in_multi_view_view1", nil, t)
|
||||
if !tryAddArangoSearchLink(ctx, db, v1, "col_in_multi_view", t) {
|
||||
t.Error("Link does not exists")
|
||||
}
|
||||
v2 := ensureArangoSearchView(ctx, db, "col_in_multi_view_view2", nil, t)
|
||||
if !tryAddArangoSearchLink(ctx, db, v2, "col_in_multi_view", t) {
|
||||
t.Error("Link does not exists")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddCollectionMultipleViews creates a collection and two view. adds the collection to both views
|
||||
// and checks if the links exist. The links are set when creating the view.
|
||||
func TestAddCollectionMultipleViewsViaCreate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "col_in_multi_view_create_test", nil, t)
|
||||
ensureCollection(ctx, db, "col_in_multi_view_create", nil, t)
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"col_in_multi_view_create": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
v1 := ensureArangoSearchView(ctx, db, "col_in_multi_view_view1", opts, t)
|
||||
if !checkLinkExists(ctx, v1, "col_in_multi_view_create", t) {
|
||||
t.Error("Link does not exists")
|
||||
}
|
||||
v2 := ensureArangoSearchView(ctx, db, "col_in_multi_view_view2", opts, t)
|
||||
if !checkLinkExists(ctx, v2, "col_in_multi_view_create", t) {
|
||||
t.Error("Link does not exists")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetArangoSearchView creates an arangosearch view and then gets it again.
|
||||
func TestGetArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
col := ensureCollection(ctx, db, "someCol", nil, t)
|
||||
name := "test_get_asview"
|
||||
opts := &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"someCol": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
}
|
||||
if _, err := db.CreateArangoSearchView(ctx, name, opts); err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", name, describe(err))
|
||||
}
|
||||
// Get view
|
||||
v, err := db.View(ctx, name)
|
||||
if err != nil {
|
||||
t.Fatalf("View('%s') failed: %s", name, describe(err))
|
||||
}
|
||||
asv, err := v.ArangoSearchView()
|
||||
if err != nil {
|
||||
t.Fatalf("ArangoSearchView() failed: %s", describe(err))
|
||||
}
|
||||
// Check v.Name
|
||||
if actualName := v.Name(); actualName != name {
|
||||
t.Errorf("Name() failed. Got '%s', expected '%s'", actualName, name)
|
||||
}
|
||||
// Check asv properties
|
||||
p, err := asv.Properties(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Properties failed: %s", describe(err))
|
||||
}
|
||||
if len(p.Links) != 1 {
|
||||
t.Errorf("Expected 1 link, got %d", len(p.Links))
|
||||
}
|
||||
// Check indexes on collection
|
||||
indexes, err := col.Indexes(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Indexes() failed: %s", describe(err))
|
||||
}
|
||||
if len(indexes) != 1 {
|
||||
// 1 is always added by the system
|
||||
t.Errorf("Expected 1 index, got %d", len(indexes))
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetArangoSearchViews creates several arangosearch views and then gets all of them.
|
||||
func TestGetArangoSearchViews(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
// Get views before adding some
|
||||
before, err := db.Views(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Views failed: %s", describe(err))
|
||||
}
|
||||
// Create views
|
||||
names := make([]string, 5)
|
||||
for i := 0; i < len(names); i++ {
|
||||
names[i] = fmt.Sprintf("test_get_views_%d", i)
|
||||
if _, err := db.CreateArangoSearchView(ctx, names[i], nil); err != nil {
|
||||
t.Fatalf("Failed to create view '%s': %s", names[i], describe(err))
|
||||
}
|
||||
}
|
||||
// Get views
|
||||
after, err := db.Views(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Views failed: %s", describe(err))
|
||||
}
|
||||
// Check count
|
||||
if len(before)+len(names) != len(after) {
|
||||
t.Errorf("Expected %d views, got %d", len(before)+len(names), len(after))
|
||||
}
|
||||
// Check view names
|
||||
for _, n := range names {
|
||||
found := false
|
||||
for _, v := range after {
|
||||
if v.Name() == n {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
t.Errorf("Expected view '%s' is not found", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemoveArangoSearchView creates an arangosearch view and then removes it.
|
||||
func TestRemoveArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(ctx, c, "view_test", nil, t)
|
||||
name := "test_remove_asview"
|
||||
v, err := db.CreateArangoSearchView(ctx, name, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create collection '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if !found {
|
||||
t.Errorf("ViewExists('%s') return false, expected true", name)
|
||||
}
|
||||
// Now remove it
|
||||
if err := v.Remove(ctx); err != nil {
|
||||
t.Fatalf("Failed to remove view '%s': %s", name, describe(err))
|
||||
}
|
||||
// View must not exist now
|
||||
if found, err := db.ViewExists(ctx, name); err != nil {
|
||||
t.Errorf("ViewExists('%s') failed: %s", name, describe(err))
|
||||
} else if found {
|
||||
t.Errorf("ViewExists('%s') return true, expected false", name)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUseArangoSearchView tries to create a view and actually use it in
|
||||
// an AQL query.
|
||||
func TestUseArangoSearchView(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
c := createClientFromEnv(t, true)
|
||||
skipBelowVersion(c, "3.4", t)
|
||||
db := ensureDatabase(nil, c, "view_test", nil, t)
|
||||
col := ensureCollection(ctx, db, "some_collection", nil, t)
|
||||
|
||||
ensureArangoSearchView(ctx, db, "some_view", &driver.ArangoSearchViewProperties{
|
||||
Links: driver.ArangoSearchLinks{
|
||||
"some_collection": driver.ArangoSearchElementProperties{
|
||||
Fields: driver.ArangoSearchFields{
|
||||
"name": driver.ArangoSearchElementProperties{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, t)
|
||||
|
||||
docs := []UserDoc{
|
||||
UserDoc{
|
||||
"John",
|
||||
23,
|
||||
},
|
||||
UserDoc{
|
||||
"Alice",
|
||||
43,
|
||||
},
|
||||
UserDoc{
|
||||
"Helmut",
|
||||
56,
|
||||
},
|
||||
}
|
||||
|
||||
_, errs, err := col.CreateDocuments(ctx, docs)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create new documents: %s", describe(err))
|
||||
} else if err := errs.FirstNonNil(); err != nil {
|
||||
t.Fatalf("Expected no errors, got first: %s", describe(err))
|
||||
}
|
||||
|
||||
// now access it via AQL with waitForSync
|
||||
{
|
||||
cur, err := db.Query(driver.WithQueryCount(ctx), `FOR doc IN some_view SEARCH doc.name == "John" OPTIONS {waitForSync:true} RETURN doc`, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to query data using arangodsearch: %s", describe(err))
|
||||
} else if cur.Count() != 1 || !cur.HasMore() {
|
||||
t.Fatalf("Wrong number of return values: expected 1, found %d", cur.Count())
|
||||
}
|
||||
|
||||
var doc UserDoc
|
||||
_, err = cur.ReadDocument(ctx, &doc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read document: %s", describe(err))
|
||||
}
|
||||
|
||||
if doc.Name != "John" {
|
||||
t.Fatalf("Expected result `John`, found `%s`", doc.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// now access it via AQL without waitForSync
|
||||
{
|
||||
cur, err := db.Query(driver.WithQueryCount(ctx), `FOR doc IN some_view SEARCH doc.name == "John" RETURN doc`, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to query data using arangodsearch: %s", describe(err))
|
||||
} else if cur.Count() != 1 || !cur.HasMore() {
|
||||
t.Fatalf("Wrong number of return values: expected 1, found %d", cur.Count())
|
||||
}
|
||||
|
||||
var doc UserDoc
|
||||
_, err = cur.ReadDocument(ctx, &doc)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to read document: %s", describe(err))
|
||||
}
|
||||
|
||||
if doc.Name != "John" {
|
||||
t.Fatalf("Expected result `John`, found `%s`", doc.Name)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -66,6 +66,9 @@ func (c *vertexCollection) readDocument(ctx context.Context, key string, result
|
|||
if err := resp.CheckStatus(200); err != nil {
|
||||
return DocumentMeta{}, contextSettings{}, WithStack(err)
|
||||
}
|
||||
// Concerns: ReadDocuments reads multiple documents via multiple calls to readDocument (this function).
|
||||
// Currently with AllowDirtyReads the wasDirtyFlag is only set according to the last read request.
|
||||
loadContextResponseValues(cs, resp)
|
||||
// Parse metadata
|
||||
var meta DocumentMeta
|
||||
if err := resp.ParseBody("vertex", &meta); err != nil {
|
||||
|
|
48
deps/github.com/arangodb/go-driver/view.go
vendored
Normal file
48
deps/github.com/arangodb/go-driver/view.go
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// View provides access to the information of a view.
|
||||
// Views are only available in ArangoDB 3.4 and higher.
|
||||
type View interface {
|
||||
// Name returns the name of the view.
|
||||
Name() string
|
||||
|
||||
// Type returns the type of this view.
|
||||
Type() ViewType
|
||||
|
||||
// ArangoSearchView returns this view as an ArangoSearch view.
|
||||
// When the type of the view is not ArangoSearch, an error is returned.
|
||||
ArangoSearchView() (ArangoSearchView, error)
|
||||
|
||||
// Database returns the database containing the view.
|
||||
Database() Database
|
||||
|
||||
// Remove removes the entire view.
|
||||
// If the view does not exist, a NotFoundError is returned.
|
||||
Remove(ctx context.Context) error
|
||||
}
|
130
deps/github.com/arangodb/go-driver/view_arangosearch.go
vendored
Normal file
130
deps/github.com/arangodb/go-driver/view_arangosearch.go
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// ArangoSearchView provides access to the information of a view.
|
||||
// Views are only available in ArangoDB 3.4 and higher.
|
||||
type ArangoSearchView interface {
|
||||
// Include generic View functions
|
||||
View
|
||||
|
||||
// Properties fetches extended information about the view.
|
||||
Properties(ctx context.Context) (ArangoSearchViewProperties, error)
|
||||
|
||||
// SetProperties changes properties of the view.
|
||||
SetProperties(ctx context.Context, options ArangoSearchViewProperties) error
|
||||
}
|
||||
|
||||
// ArangoSearchViewProperties contains properties an an ArangoSearch view.
|
||||
type ArangoSearchViewProperties struct {
|
||||
// CleanupIntervalStep specifies the minimum number of commits to wait between
|
||||
// removing unused files in the data directory.
|
||||
// Defaults to 10.
|
||||
// Use 0 to disable waiting.
|
||||
// For the case where the consolidation policies merge segments often
|
||||
// (i.e. a lot of commit+consolidate), a lower value will cause a lot of
|
||||
// disk space to be wasted.
|
||||
// For the case where the consolidation policies rarely merge segments
|
||||
// (i.e. few inserts/deletes), a higher value will impact performance
|
||||
// without any added benefits.
|
||||
CleanupIntervalStep int64 `json:"cleanupIntervalStep,omitempty"`
|
||||
// ConsolidationInterval specifies the minimum number of milliseconds that must be waited
|
||||
// between committing index data changes and making them visible to queries.
|
||||
// Defaults to 60000.
|
||||
// Use 0 to disable.
|
||||
// For the case where there are a lot of inserts/updates, a lower value,
|
||||
// until commit, will cause the index not to account for them and memory usage
|
||||
// would continue to grow.
|
||||
// For the case where there are a few inserts/updates, a higher value will
|
||||
// impact performance and waste disk space for each commit call without
|
||||
// any added benefits.
|
||||
ConsolidationInterval int64 `json:"consolidationIntervalMsec,omitempty"`
|
||||
// ConsolidationPolicy specifies thresholds for consolidation.
|
||||
ConsolidationPolicy *ArangoSearchConsolidationPolicy `json:"consolidationPolicy,omitempty"`
|
||||
|
||||
/*
|
||||
// Locale specifies the default locale used for queries on analyzed string values.
|
||||
// Defaults to "C". TODO What is that?
|
||||
Locale Locale `json:"locale,omitempty"`
|
||||
*/
|
||||
// Links contains the properties for how individual collections
|
||||
// are indexed in thie view.
|
||||
// The key of the map are collection names.
|
||||
Links ArangoSearchLinks `json:"links,omitempty"`
|
||||
}
|
||||
|
||||
// Locale is a strongly typed specifier of a locale.
|
||||
// TODO specify semantics.
|
||||
type Locale string
|
||||
|
||||
// ArangoSearchConsolidationPolicy holds threshold values specifying when to
|
||||
// consolidate view data.
|
||||
// Semantics of the values depend on where they are used.
|
||||
type ArangoSearchConsolidationPolicy struct {
|
||||
// Threshold is a percentage (0..1)
|
||||
Threshold float64 `json:"threshold,omitempty"`
|
||||
// SegmentThreshold is an absolute value.
|
||||
SegmentThreshold int64 `json:"segmentThreshold,omitempty"`
|
||||
}
|
||||
|
||||
// ArangoSearchLinks is a strongly typed map containing links between a
|
||||
// collection and a view.
|
||||
// The keys in the map are collection names.
|
||||
type ArangoSearchLinks map[string]ArangoSearchElementProperties
|
||||
|
||||
// ArangoSearchFields is a strongly typed map containing properties per field.
|
||||
// The keys in the map are field names.
|
||||
type ArangoSearchFields map[string]ArangoSearchElementProperties
|
||||
|
||||
// ArangoSearchElementProperties contains properties that specify how an element
|
||||
// is indexed in an ArangoSearch view.
|
||||
// Note that this structure is recursive. Settings not specified (nil)
|
||||
// at a given level will inherit their setting from a lower level.
|
||||
type ArangoSearchElementProperties struct {
|
||||
// The list of analyzers to be used for indexing of string values. Defaults to ["identify"].
|
||||
Analyzers []string `json:"analyzers,omitempty"`
|
||||
// If set to true, all fields of this element will be indexed. Defaults to false.
|
||||
IncludeAllFields *bool `json:"includeAllFields,omitempty"`
|
||||
// If set to true, values in a listed are treated as separate values. Defaults to false.
|
||||
TrackListPositions *bool `json:"trackListPositions,omitempty"`
|
||||
// This values specifies how the view should track values.
|
||||
StoreValues ArangoSearchStoreValues `json:"storeValues,omitempty"`
|
||||
// Fields contains the properties for individual fields of the element.
|
||||
// The key of the map are field names.
|
||||
Fields ArangoSearchFields `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// ArangoSearchStoreValues is the type of the StoreValues option of an ArangoSearch element.
|
||||
type ArangoSearchStoreValues string
|
||||
|
||||
const (
|
||||
// ArangoSearchStoreValuesNone specifies that a view should not store values.
|
||||
ArangoSearchStoreValuesNone ArangoSearchStoreValues = "none"
|
||||
// ArangoSearchStoreValuesID specifies that a view should only store
|
||||
// information about value presence, to allow use of the EXISTS() function.
|
||||
ArangoSearchStoreValuesID ArangoSearchStoreValues = "id"
|
||||
)
|
74
deps/github.com/arangodb/go-driver/view_arangosearch_impl.go
vendored
Normal file
74
deps/github.com/arangodb/go-driver/view_arangosearch_impl.go
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
)
|
||||
|
||||
// viewArangoSearch implements ArangoSearchView
|
||||
type viewArangoSearch struct {
|
||||
view
|
||||
}
|
||||
|
||||
// Properties fetches extended information about the view.
|
||||
func (v *viewArangoSearch) Properties(ctx context.Context) (ArangoSearchViewProperties, error) {
|
||||
req, err := v.conn.NewRequest("GET", path.Join(v.relPath(), "properties"))
|
||||
if err != nil {
|
||||
return ArangoSearchViewProperties{}, WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := v.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return ArangoSearchViewProperties{}, WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return ArangoSearchViewProperties{}, WithStack(err)
|
||||
}
|
||||
var data ArangoSearchViewProperties
|
||||
if err := resp.ParseBody("", &data); err != nil {
|
||||
return ArangoSearchViewProperties{}, WithStack(err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// SetProperties changes properties of the view.
|
||||
func (v *viewArangoSearch) SetProperties(ctx context.Context, options ArangoSearchViewProperties) error {
|
||||
req, err := v.conn.NewRequest("PUT", path.Join(v.relPath(), "properties"))
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
if _, err := req.SetBody(options); err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := v.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
104
deps/github.com/arangodb/go-driver/view_impl.go
vendored
Normal file
104
deps/github.com/arangodb/go-driver/view_impl.go
vendored
Normal file
|
@ -0,0 +1,104 @@
|
|||
//
|
||||
// DISCLAIMER
|
||||
//
|
||||
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Licensed 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.
|
||||
//
|
||||
// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||
//
|
||||
// Author Ewout Prangsma
|
||||
//
|
||||
|
||||
package driver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
// newView creates a new View implementation.
|
||||
func newView(name string, viewType ViewType, db *database) (View, error) {
|
||||
if name == "" {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "name is empty"})
|
||||
}
|
||||
if viewType == "" {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "viewType is empty"})
|
||||
}
|
||||
if db == nil {
|
||||
return nil, WithStack(InvalidArgumentError{Message: "db is nil"})
|
||||
}
|
||||
return &view{
|
||||
name: name,
|
||||
viewType: viewType,
|
||||
db: db,
|
||||
conn: db.conn,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type view struct {
|
||||
name string
|
||||
viewType ViewType
|
||||
db *database
|
||||
conn Connection
|
||||
}
|
||||
|
||||
// relPath creates the relative path to this view (`_db/<db-name>/_api/view/<view-name>`)
|
||||
func (v *view) relPath() string {
|
||||
escapedName := pathEscape(v.name)
|
||||
return path.Join(v.db.relPath(), "_api", "view", escapedName)
|
||||
}
|
||||
|
||||
// Name returns the name of the view.
|
||||
func (v *view) Name() string {
|
||||
return v.name
|
||||
}
|
||||
|
||||
// Type returns the type of this view.
|
||||
func (v *view) Type() ViewType {
|
||||
return v.viewType
|
||||
}
|
||||
|
||||
// ArangoSearchView returns this view as an ArangoSearch view.
|
||||
// When the type of the view is not ArangoSearch, an error is returned.
|
||||
func (v *view) ArangoSearchView() (ArangoSearchView, error) {
|
||||
if v.viewType != ViewTypeArangoSearch {
|
||||
return nil, WithStack(newArangoError(http.StatusConflict, 0, fmt.Sprintf("Type must be '%s', got '%s'", ViewTypeArangoSearch, v.viewType)))
|
||||
}
|
||||
return &viewArangoSearch{view: *v}, nil
|
||||
}
|
||||
|
||||
// Database returns the database containing the view.
|
||||
func (v *view) Database() Database {
|
||||
return v.db
|
||||
}
|
||||
|
||||
// Remove removes the entire view.
|
||||
// If the view does not exist, a NotFoundError is returned.
|
||||
func (v *view) Remove(ctx context.Context) error {
|
||||
req, err := v.conn.NewRequest("DELETE", v.relPath())
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
applyContextSettings(ctx, req)
|
||||
resp, err := v.conn.Do(ctx, req)
|
||||
if err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
if err := resp.CheckStatus(200); err != nil {
|
||||
return WithStack(err)
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -43,6 +43,16 @@ type vstRequest struct {
|
|||
written bool
|
||||
}
|
||||
|
||||
// Path returns the Request path
|
||||
func (r *vstRequest) Path() string {
|
||||
return r.path
|
||||
}
|
||||
|
||||
// Method returns the Request method
|
||||
func (r *vstRequest) Method() string {
|
||||
return r.method
|
||||
}
|
||||
|
||||
// Clone creates a new request containing the same data as this request
|
||||
func (r *vstRequest) Clone() driver.Request {
|
||||
clone := *r
|
||||
|
|
Loading…
Reference in a new issue