diff --git a/deps/github.com/arangodb/go-driver/CHANGELOG.md b/deps/github.com/arangodb/go-driver/CHANGELOG.md new file mode 100644 index 000000000..239819a32 --- /dev/null +++ b/deps/github.com/arangodb/go-driver/CHANGELOG.md @@ -0,0 +1,133 @@ +# Change Log + +## [Master](https://github.com/arangodb/go-driver/tree/HEAD) + +**Closed issues:** + +- Structs with key specified don't read the key [\#138](https://github.com/arangodb/go-driver/issues/138) +- Edge document with user provided key is inserted as many times as the number of shards [\#137](https://github.com/arangodb/go-driver/issues/137) +- Query "return null" make the panic: runtime error [\#117](https://github.com/arangodb/go-driver/issues/117) +- driver does not seem to decode numeric timestamps to time.Time [\#102](https://github.com/arangodb/go-driver/issues/102) +- stable and full feature? [\#100](https://github.com/arangodb/go-driver/issues/100) +- collection with "Wait for sync:" setting, Create-/UpdateDocument return: ArangoError: Code 0, ErrorNum 0 [\#96](https://github.com/arangodb/go-driver/issues/96) +- Error when connecting to ArangoDB with SSL enabled [\#95](https://github.com/arangodb/go-driver/issues/95) +- Possible concurrency issues, using VST connection [\#86](https://github.com/arangodb/go-driver/issues/86) +- An example of a transactional request [\#84](https://github.com/arangodb/go-driver/issues/84) +- Multi-tenancy connection management [\#83](https://github.com/arangodb/go-driver/issues/83) +- Cursor returned from a query with empty Count [\#82](https://github.com/arangodb/go-driver/issues/82) +- CONTRIBUTING.md [\#79](https://github.com/arangodb/go-driver/issues/79) +- Querying documents + [\#76](https://github.com/arangodb/go-driver/issues/76) +- Is there an ArangoDB-Object on Struct? [\#75](https://github.com/arangodb/go-driver/issues/75) +- DB Session [\#73](https://github.com/arangodb/go-driver/issues/73) +- correct way to get multiple documents ? [\#70](https://github.com/arangodb/go-driver/issues/70) +- Revalidate whether workaround can be removed [\#68](https://github.com/arangodb/go-driver/issues/68) +- Makefile `SWTSECRET` vs `JWTSECRET` [\#66](https://github.com/arangodb/go-driver/issues/66) +- Implement transactions [\#56](https://github.com/arangodb/go-driver/issues/56) +- bulk import API [\#55](https://github.com/arangodb/go-driver/issues/55) +- Question: how to write auto-inc feild? [\#51](https://github.com/arangodb/go-driver/issues/51) +- add index attribute for new feature in arangodb3.2 [\#48](https://github.com/arangodb/go-driver/issues/48) +- Unable to connect to server in macos [\#41](https://github.com/arangodb/go-driver/issues/41) +- Handle 401 status code before checking content type. [\#38](https://github.com/arangodb/go-driver/issues/38) +- Support for raw string in query return [\#37](https://github.com/arangodb/go-driver/issues/37) +- \[ArangoDB Server 3.1.6\] Unsupported content type: client.DatabaseExists when Database already exists [\#36](https://github.com/arangodb/go-driver/issues/36) +- Can't connect because of 'Unsupported content type'? [\#35](https://github.com/arangodb/go-driver/issues/35) +- cursor implementation when running queries with non-Document return values [\#20](https://github.com/arangodb/go-driver/issues/20) +- Make failover with cursors more explicit [\#16](https://github.com/arangodb/go-driver/issues/16) +- Add non-context version of method calls [\#7](https://github.com/arangodb/go-driver/issues/7) + +**Merged pull requests:** + +- Properly closing idle VST connections [\#139](https://github.com/arangodb/go-driver/pull/139) +- Improving performance of reading the body of large responses [\#134](https://github.com/arangodb/go-driver/pull/134) +- Added Collection.ReadDocuments [\#133](https://github.com/arangodb/go-driver/pull/133) +- Added support for fetching job ID in CleanoutServer [\#131](https://github.com/arangodb/go-driver/pull/131) +- Exclude high load test on VST+3.2 [\#129](https://github.com/arangodb/go-driver/pull/129) +- Test/prevent concurrent vst read write [\#128](https://github.com/arangodb/go-driver/pull/128) +- Added single+ssl tests. All tests now use starter [\#127](https://github.com/arangodb/go-driver/pull/127) +- Bugfix/read chunk loop [\#126](https://github.com/arangodb/go-driver/pull/126) +- Prevent possible endless read chunk look in in VST connection [\#125](https://github.com/arangodb/go-driver/pull/125) +- Fixing Query\(return nul\) resulting in panic [\#124](https://github.com/arangodb/go-driver/pull/124) +- Added WithAllowNoLeader [\#123](https://github.com/arangodb/go-driver/pull/123) +- Added Cluster.RemoveServer [\#122](https://github.com/arangodb/go-driver/pull/122) +- Added mutex guarding message chunks. [\#121](https://github.com/arangodb/go-driver/pull/121) +- Documentation/go refactor [\#120](https://github.com/arangodb/go-driver/pull/120) +- VST stream cursor test fixes & VST fail-quick fix [\#119](https://github.com/arangodb/go-driver/pull/119) +- Prevent a VST connection from being used before its configuration callback has finished [\#118](https://github.com/arangodb/go-driver/pull/118) +- Fixed AgencyConnection in the context of authentication [\#116](https://github.com/arangodb/go-driver/pull/116) +- Adding timeout for streaming cursor test [\#115](https://github.com/arangodb/go-driver/pull/115) +- Added package level docs [\#114](https://github.com/arangodb/go-driver/pull/114) +- Close the connection when the initial onCreatedCallback fails [\#113](https://github.com/arangodb/go-driver/pull/113) +- Adding extra concurrency-safety for VST [\#112](https://github.com/arangodb/go-driver/pull/112) +- Added upper limit to the number of concurrent requests to a single server. [\#111](https://github.com/arangodb/go-driver/pull/111) +- Added support for multiple VST connections per server [\#110](https://github.com/arangodb/go-driver/pull/110) +- Fixed expected status code for operations on collections that have waitForSync enabled [\#109](https://github.com/arangodb/go-driver/pull/109) +- Added helper to determine agency health [\#108](https://github.com/arangodb/go-driver/pull/108) +- Added ServerID function [\#107](https://github.com/arangodb/go-driver/pull/107) +- Added exclusive lock using agency [\#106](https://github.com/arangodb/go-driver/pull/106) +- Added helper for JWT secret based authentication [\#105](https://github.com/arangodb/go-driver/pull/105) +- Added Agency API [\#104](https://github.com/arangodb/go-driver/pull/104) +- Added option to not follow redirects and return the original response [\#103](https://github.com/arangodb/go-driver/pull/103) +- Add support for stream query cursor [\#101](https://github.com/arangodb/go-driver/pull/101) +- Active-Failover with Velocystream [\#99](https://github.com/arangodb/go-driver/pull/99) +- Added API functions for shutting down a server and cleaning it out [\#98](https://github.com/arangodb/go-driver/pull/98) +- Skip replication test on cluster [\#94](https://github.com/arangodb/go-driver/pull/94) +- Documented behavior for custom http.Transport wrt MaxIdleConnsPerHost field [\#93](https://github.com/arangodb/go-driver/pull/93) +- Fix ReturnOld/New for edge/vertex operations. [\#92](https://github.com/arangodb/go-driver/pull/92) +- Database.Info\(\) added [\#91](https://github.com/arangodb/go-driver/pull/91) +- Replication interface added. [\#90](https://github.com/arangodb/go-driver/pull/90) +- Grouping server specific info calls in ClientServerInfo, exposing ServerRole API [\#89](https://github.com/arangodb/go-driver/pull/89) +- Added `ReplicationFactor` to `SetCollectionPropertiesOptions` [\#88](https://github.com/arangodb/go-driver/pull/88) +- Allow for some time to reach intended status [\#87](https://github.com/arangodb/go-driver/pull/87) +- Fixing SWTSECRET -\> JWTSECRET [\#85](https://github.com/arangodb/go-driver/pull/85) +- Added CONTRIBUTING.md [\#80](https://github.com/arangodb/go-driver/pull/80) +- Resilientsingle support [\#77](https://github.com/arangodb/go-driver/pull/77) +- Adding cluster specific operations [\#72](https://github.com/arangodb/go-driver/pull/72) +- Added IsSmart, SmartGraphAttribute attributes to CreateCollectionOptions [\#71](https://github.com/arangodb/go-driver/pull/71) +- Adding Response.Header\(string\) & tests [\#69](https://github.com/arangodb/go-driver/pull/69) +- Server mode \(get/set\) [\#65](https://github.com/arangodb/go-driver/pull/65) +- Adding `WithConfigured` [\#64](https://github.com/arangodb/go-driver/pull/64) +- Added DistributeShardsLike field to CreateCollectionOptions [\#62](https://github.com/arangodb/go-driver/pull/62) +- Added WithEnforceReplicationFactor [\#61](https://github.com/arangodb/go-driver/pull/61) +- Added `WithIsSystem` [\#60](https://github.com/arangodb/go-driver/pull/60) +- Support synchronising endpoints on resilient single server mode [\#59](https://github.com/arangodb/go-driver/pull/59) +- Added `WithIgnoreRevisions` [\#58](https://github.com/arangodb/go-driver/pull/58) +- Basic transaction implementation [\#57](https://github.com/arangodb/go-driver/pull/57) +- Support `x-arango-dump` content type [\#54](https://github.com/arangodb/go-driver/pull/54) +- Added WithIsRestore \(not internal for normal client use!!!!\) [\#53](https://github.com/arangodb/go-driver/pull/53) +- Raw authentication [\#52](https://github.com/arangodb/go-driver/pull/52) +- Include travis tests for arangodb 3.1 [\#50](https://github.com/arangodb/go-driver/pull/50) +- Added NoDeduplicate field for hash & skiplist index options [\#49](https://github.com/arangodb/go-driver/pull/49) +- Supporting additional user access functions [\#47](https://github.com/arangodb/go-driver/pull/47) +- ftKnox - fix sprintf conversions [\#42](https://github.com/arangodb/go-driver/pull/42) +- Convert 401 text/plain response to proper ArangoError [\#39](https://github.com/arangodb/go-driver/pull/39) +- Adding Storage engine detection [\#34](https://github.com/arangodb/go-driver/pull/34) +- Starter update [\#33](https://github.com/arangodb/go-driver/pull/33) +- Fix reading the response body to ensure keep-alive [\#32](https://github.com/arangodb/go-driver/pull/32) +- Velocy stream support \(wip\) [\#31](https://github.com/arangodb/go-driver/pull/31) +- Supporting Velocypack content-type \(instead of JSON\) \(wip\) [\#29](https://github.com/arangodb/go-driver/pull/29) +- Fixed Cursor.ReadDocument for queries returning non-documents [\#27](https://github.com/arangodb/go-driver/pull/27) +- Added Collection.Statistics [\#25](https://github.com/arangodb/go-driver/pull/25) +- Adding Database.ValidateQuery [\#24](https://github.com/arangodb/go-driver/pull/24) +- Added Collection.DocumentExists [\#23](https://github.com/arangodb/go-driver/pull/23) +- Added `Database.Remove\(\)` [\#22](https://github.com/arangodb/go-driver/pull/22) +- Changing graph API to introduce VertexConstraints [\#21](https://github.com/arangodb/go-driver/pull/21) +- Endpoint reconfiguration [\#19](https://github.com/arangodb/go-driver/pull/19) +- Allow custom http.RoundTripper [\#18](https://github.com/arangodb/go-driver/pull/18) +- Added WithEndpoint, used to force a specific endpoint for a request. [\#17](https://github.com/arangodb/go-driver/pull/17) +- Adding failover tests [\#15](https://github.com/arangodb/go-driver/pull/15) +- Adding simple performance benchmarks [\#14](https://github.com/arangodb/go-driver/pull/14) +- Cluster tests [\#12](https://github.com/arangodb/go-driver/pull/12) +- ImportDocuments [\#11](https://github.com/arangodb/go-driver/pull/11) +- Adding Graph support \(wip\) [\#10](https://github.com/arangodb/go-driver/pull/10) +- Added collection status,count,rename,load,unload,truncate,properties [\#9](https://github.com/arangodb/go-driver/pull/9) +- Adding user API [\#8](https://github.com/arangodb/go-driver/pull/8) +- Adding index support [\#6](https://github.com/arangodb/go-driver/pull/6) +- Added Cursor support [\#5](https://github.com/arangodb/go-driver/pull/5) +- Adding multi document requests [\#4](https://github.com/arangodb/go-driver/pull/4) +- Creating interface. \(WIP\) [\#3](https://github.com/arangodb/go-driver/pull/3) +- Revert "Creating interface" [\#2](https://github.com/arangodb/go-driver/pull/2) +- Creating interface [\#1](https://github.com/arangodb/go-driver/pull/1) + + + +\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* \ No newline at end of file diff --git a/deps/github.com/arangodb/go-driver/MAINTAINERS.md b/deps/github.com/arangodb/go-driver/MAINTAINERS.md new file mode 100644 index 000000000..7e368ad50 --- /dev/null +++ b/deps/github.com/arangodb/go-driver/MAINTAINERS.md @@ -0,0 +1,11 @@ +# Maintainer Instructions + +- Always preserve backward compatibility +- Build using `make clean && make` +- After merging PR, alway run `make changelog` and commit changes +- Set ArangoDB docker container (used for testing) using `export ARANGODB=` +- Run tests using: + - `make run-tests-single` + - `make run-tests-resilientsingle` + - `make run-tests-cluster`. +- Always create changes in a PR diff --git a/deps/github.com/arangodb/go-driver/Makefile b/deps/github.com/arangodb/go-driver/Makefile index b8f6f00d2..d49c309ef 100644 --- a/deps/github.com/arangodb/go-driver/Makefile +++ b/deps/github.com/arangodb/go-driver/Makefile @@ -113,6 +113,17 @@ $(GOBUILDDIR): GOPATH=$(GOBUILDDIR) go get github.com/arangodb/go-velocypack GOPATH=$(GOBUILDDIR) go get github.com/dgrijalva/jwt-go +.PHONY: changelog +changelog: + @docker run --rm \ + -e CHANGELOG_GITHUB_TOKEN=$(shell cat ~/.arangodb/github-token) \ + -v "$(ROOTDIR)":/usr/local/src/your-app \ + ferrarimarco/github-changelog-generator \ + --user arangodb \ + --project go-driver \ + --no-author \ + --unreleased-label "Master" + run-tests: run-tests-http run-tests-single run-tests-resilientsingle run-tests-cluster # Tests of HTTP package diff --git a/deps/github.com/arangodb/go-driver/collection_document_impl.go b/deps/github.com/arangodb/go-driver/collection_document_impl.go index bc09aa5f7..6ed995708 100644 --- a/deps/github.com/arangodb/go-driver/collection_document_impl.go +++ b/deps/github.com/arangodb/go-driver/collection_document_impl.go @@ -80,6 +80,55 @@ func (c *collection) ReadDocument(ctx context.Context, key string, result interf return meta, nil } +// ReadDocuments reads multiple documents with given keys from the collection. +// The documents data is stored into elements of the given results slice, +// the documents meta data is returned. +// If no document exists with a given key, a NotFoundError is returned at its errors index. +func (c *collection) ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error) { + resultsVal := reflect.ValueOf(results) + switch resultsVal.Kind() { + case reflect.Array, reflect.Slice: + // OK + default: + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("results data must be of kind Array, got %s", resultsVal.Kind())}) + } + if keys == nil { + return nil, nil, WithStack(InvalidArgumentError{Message: "keys nil"}) + } + resultCount := resultsVal.Len() + if len(keys) != resultCount { + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", resultCount, len(keys))}) + } + for _, key := range keys { + if err := validateKey(key); err != nil { + return nil, nil, WithStack(err) + } + } + req, err := c.conn.NewRequest("PUT", c.relPath("document")) + if err != nil { + return nil, nil, WithStack(err) + } + req = req.SetQuery("onlyget", "1") + cs := applyContextSettings(ctx, req) + if _, err := req.SetBodyArray(keys, nil); err != nil { + return nil, nil, WithStack(err) + } + resp, err := c.conn.Do(ctx, req) + if err != nil { + return nil, nil, WithStack(err) + } + if err := resp.CheckStatus(200); err != nil { + return nil, nil, WithStack(err) + } + // Parse response array + metas, errs, err := parseResponseArray(resp, resultCount, cs, results) + if err != nil { + return nil, nil, WithStack(err) + } + return metas, errs, nil + +} + // CreateDocument creates a single document in the collection. // The document data is loaded from the given document, the document meta data is returned. // If the document data already contains a `_key` field, this will be used as key of the new document, @@ -163,7 +212,7 @@ func (c *collection) CreateDocuments(ctx context.Context, documents interface{}) return nil, nil, nil } // Parse response array - metas, errs, err := parseResponseArray(resp, documentCount, cs) + metas, errs, err := parseResponseArray(resp, documentCount, cs, nil) if err != nil { return nil, nil, WithStack(err) } @@ -272,7 +321,7 @@ func (c *collection) UpdateDocuments(ctx context.Context, keys []string, updates return nil, nil, nil } // Parse response array - metas, errs, err := parseResponseArray(resp, updateCount, cs) + metas, errs, err := parseResponseArray(resp, updateCount, cs, nil) if err != nil { return nil, nil, WithStack(err) } @@ -381,7 +430,7 @@ func (c *collection) ReplaceDocuments(ctx context.Context, keys []string, docume return nil, nil, nil } // Parse response array - metas, errs, err := parseResponseArray(resp, documentCount, cs) + metas, errs, err := parseResponseArray(resp, documentCount, cs, nil) if err != nil { return nil, nil, WithStack(err) } @@ -464,7 +513,7 @@ func (c *collection) RemoveDocuments(ctx context.Context, keys []string) (Docume return nil, nil, nil } // Parse response array - metas, errs, err := parseResponseArray(resp, keyCount, cs) + metas, errs, err := parseResponseArray(resp, keyCount, cs, nil) if err != nil { return nil, nil, WithStack(err) } @@ -572,7 +621,7 @@ func createMergeArray(keys, revs []string) ([]map[string]interface{}, error) { } // parseResponseArray parses an array response in the given response -func parseResponseArray(resp Response, count int, cs contextSettings) (DocumentMetaSlice, ErrorSlice, error) { +func parseResponseArray(resp Response, count int, cs contextSettings, results interface{}) (DocumentMetaSlice, ErrorSlice, error) { resps, err := resp.ParseArrayBody() if err != nil { return nil, nil, WithStack(err) @@ -581,6 +630,7 @@ func parseResponseArray(resp Response, count int, cs contextSettings) (DocumentM errs := make(ErrorSlice, count) returnOldVal := reflect.ValueOf(cs.ReturnOld) returnNewVal := reflect.ValueOf(cs.ReturnNew) + resultsVal := reflect.ValueOf(results) for i := 0; i < count; i++ { resp := resps[i] var meta DocumentMeta @@ -606,6 +656,13 @@ func parseResponseArray(resp Response, count int, cs contextSettings) (DocumentM } } } + if results != nil { + // Parse compare result document + resultsEntryVal := resultsVal.Index(i).Addr() + if err := resp.ParseBody("", resultsEntryVal.Interface()); err != nil { + errs[i] = err + } + } } } return metas, errs, nil diff --git a/deps/github.com/arangodb/go-driver/collection_documents.go b/deps/github.com/arangodb/go-driver/collection_documents.go index aef8bc2ef..a600a053c 100644 --- a/deps/github.com/arangodb/go-driver/collection_documents.go +++ b/deps/github.com/arangodb/go-driver/collection_documents.go @@ -34,6 +34,12 @@ type CollectionDocuments interface { // If no document exists with given key, a NotFoundError is returned. ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error) + // ReadDocuments reads multiple documents with given keys from the collection. + // The documents data is stored into elements of the given results slice, + // the documents meta data is returned. + // If no document exists with a given key, a NotFoundError is returned at its errors index. + ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error) + // CreateDocument creates a single document in the collection. // The document data is loaded from the given document, the document meta data is returned. // If the document data already contains a `_key` field, this will be used as key of the new document, diff --git a/deps/github.com/arangodb/go-driver/edge_collection_documents_impl.go b/deps/github.com/arangodb/go-driver/edge_collection_documents_impl.go index 100526953..395cb32b5 100644 --- a/deps/github.com/arangodb/go-driver/edge_collection_documents_impl.go +++ b/deps/github.com/arangodb/go-driver/edge_collection_documents_impl.go @@ -43,33 +43,89 @@ func (c *edgeCollection) DocumentExists(ctx context.Context, key string) (bool, // The document data is stored into result, the document meta data is returned. // If no document exists with given key, a NotFoundError is returned. func (c *edgeCollection) ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error) { - if err := validateKey(key); err != nil { + meta, _, err := c.readDocument(ctx, key, result) + if err != nil { return DocumentMeta{}, WithStack(err) } + return meta, nil +} + +func (c *edgeCollection) readDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, contextSettings, error) { + if err := validateKey(key); err != nil { + return DocumentMeta{}, contextSettings{}, WithStack(err) + } escapedKey := pathEscape(key) req, err := c.conn.NewRequest("GET", path.Join(c.relPath(), escapedKey)) if err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } + cs := applyContextSettings(ctx, req) resp, err := c.conn.Do(ctx, req) if err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } if err := resp.CheckStatus(200); err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } // Parse metadata var meta DocumentMeta if err := resp.ParseBody("edge", &meta); err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } // Parse result if result != nil { if err := resp.ParseBody("edge", result); err != nil { - return meta, WithStack(err) + return meta, contextSettings{}, WithStack(err) } } - return meta, nil + return meta, cs, nil +} + +// ReadDocuments reads multiple documents with given keys from the collection. +// The documents data is stored into elements of the given results slice, +// the documents meta data is returned. +// If no document exists with a given key, a NotFoundError is returned at its errors index. +func (c *edgeCollection) ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error) { + resultsVal := reflect.ValueOf(results) + switch resultsVal.Kind() { + case reflect.Array, reflect.Slice: + // OK + default: + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("results data must be of kind Array, got %s", resultsVal.Kind())}) + } + if keys == nil { + return nil, nil, WithStack(InvalidArgumentError{Message: "keys nil"}) + } + resultCount := resultsVal.Len() + if len(keys) != resultCount { + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", resultCount, len(keys))}) + } + for _, key := range keys { + if err := validateKey(key); err != nil { + return nil, nil, WithStack(err) + } + } + metas := make(DocumentMetaSlice, resultCount) + errs := make(ErrorSlice, resultCount) + silent := false + for i := 0; i < resultCount; i++ { + result := resultsVal.Index(i).Addr() + ctx, err := withDocumentAt(ctx, i) + if err != nil { + return nil, nil, WithStack(err) + } + key := keys[i] + meta, cs, err := c.readDocument(ctx, key, result.Interface()) + if cs.Silent { + silent = true + } else { + metas[i], errs[i] = meta, err + } + } + if silent { + return nil, nil, nil + } + return metas, errs, nil } // CreateDocument creates a single document in the collection. diff --git a/deps/github.com/arangodb/go-driver/http/connection.go b/deps/github.com/arangodb/go-driver/http/connection.go index e4c428894..aa4e3bf03 100644 --- a/deps/github.com/arangodb/go-driver/http/connection.go +++ b/deps/github.com/arangodb/go-driver/http/connection.go @@ -23,6 +23,7 @@ package http import ( + "bytes" "context" "crypto/tls" "encoding/json" @@ -273,8 +274,7 @@ func (c *httpConnection) Do(ctx context.Context, req driver.Request) (driver.Res } // Read response body - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := readBody(resp) if err != nil { return nil, driver.WithStack(err) } @@ -319,6 +319,29 @@ func (c *httpConnection) Do(ctx context.Context, req driver.Request) (driver.Res return httpResp, nil } +// readBody reads the body of the given response into a byte slice. +func readBody(resp *http.Response) ([]byte, error) { + defer resp.Body.Close() + contentLength := resp.ContentLength + if contentLength < 0 { + // Don't know the content length, do it the slowest way + result, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, driver.WithStack(err) + } + return result, nil + } + buf := &bytes.Buffer{} + if int64(int(contentLength)) == contentLength { + // contentLength is an int64. If we can safely cast to int, use Grow. + buf.Grow(int(contentLength)) + } + if _, err := buf.ReadFrom(resp.Body); err != nil { + return nil, driver.WithStack(err) + } + return buf.Bytes(), nil +} + // Unmarshal unmarshals the given raw object into the given result interface. func (c *httpConnection) Unmarshal(data driver.RawObject, result interface{}) error { ct := c.contentType diff --git a/deps/github.com/arangodb/go-driver/test/documents_create_test.go b/deps/github.com/arangodb/go-driver/test/documents_create_test.go index 30ae1e4af..1ee066acf 100644 --- a/deps/github.com/arangodb/go-driver/test/documents_create_test.go +++ b/deps/github.com/arangodb/go-driver/test/documents_create_test.go @@ -55,6 +55,21 @@ func TestCreateDocuments(t *testing.T) { } else if len(metas) != len(docs) { t.Errorf("Expected %d metas, got %d", len(docs), len(metas)) } else { + // Read back using ReadDocuments + keys := make([]string, len(docs)) + for i, m := range metas { + keys[i] = m.Key + } + readDocs := make([]UserDoc, len(docs)) + if _, _, err := col.ReadDocuments(nil, keys, readDocs); err != nil { + t.Fatalf("Failed to read documents: %s", describe(err)) + } + for i, d := range readDocs { + if !reflect.DeepEqual(docs[i], d) { + t.Errorf("Got wrong document. Expected %+v, got %+v", docs[i], d) + } + } + // Read back using individual ReadDocument requests for i := 0; i < len(docs); i++ { if err := errs[i]; err != nil { t.Errorf("Expected no error at index %d, got %s", i, describe(err)) diff --git a/deps/github.com/arangodb/go-driver/test/edges_create_test.go b/deps/github.com/arangodb/go-driver/test/edges_create_test.go index 2fab94d2a..8875b93c4 100644 --- a/deps/github.com/arangodb/go-driver/test/edges_create_test.go +++ b/deps/github.com/arangodb/go-driver/test/edges_create_test.go @@ -66,6 +66,21 @@ func TestCreateEdges(t *testing.T) { } else if len(metas) != len(docs) { t.Errorf("Expected %d metas, got %d", len(docs), len(metas)) } else { + // Read back using ReadDocuments + keys := make([]string, len(docs)) + for i, m := range metas { + keys[i] = m.Key + } + readDocs := make([]RouteEdge, len(docs)) + if _, _, err := ec.ReadDocuments(nil, keys, readDocs); err != nil { + t.Fatalf("Failed to read documents: %s", describe(err)) + } + for i, d := range readDocs { + if !reflect.DeepEqual(docs[i], d) { + t.Errorf("Got wrong document. Expected %+v, got %+v", docs[i], d) + } + } + // Read back using individual ReadDocument requests for i := 0; i < len(docs); i++ { if err := errs[i]; err != nil { t.Errorf("Expected no error at index %d, got %s", i, describe(err)) diff --git a/deps/github.com/arangodb/go-driver/test/vertices_create_test.go b/deps/github.com/arangodb/go-driver/test/vertices_create_test.go index db10b5745..bde766635 100644 --- a/deps/github.com/arangodb/go-driver/test/vertices_create_test.go +++ b/deps/github.com/arangodb/go-driver/test/vertices_create_test.go @@ -55,6 +55,21 @@ func TestCreateVertices(t *testing.T) { } else if len(metas) != len(docs) { t.Errorf("Expected %d metas, got %d", len(docs), len(metas)) } else { + // Read back using ReadDocuments + keys := make([]string, len(docs)) + for i, m := range metas { + keys[i] = m.Key + } + readDocs := make([]Book, len(docs)) + if _, _, err := vc.ReadDocuments(nil, keys, readDocs); err != nil { + t.Fatalf("Failed to read documents: %s", describe(err)) + } + for i, d := range readDocs { + if !reflect.DeepEqual(docs[i], d) { + t.Errorf("Got wrong document. Expected %+v, got %+v", docs[i], d) + } + } + // Read back using individual ReadDocument requests for i := 0; i < len(docs); i++ { if err := errs[i]; err != nil { t.Errorf("Expected no error at index %d, got %s", i, describe(err)) diff --git a/deps/github.com/arangodb/go-driver/vertex_collection_documents_impl.go b/deps/github.com/arangodb/go-driver/vertex_collection_documents_impl.go index 323e2c5b0..cf036afe6 100644 --- a/deps/github.com/arangodb/go-driver/vertex_collection_documents_impl.go +++ b/deps/github.com/arangodb/go-driver/vertex_collection_documents_impl.go @@ -42,33 +42,89 @@ func (c *vertexCollection) DocumentExists(ctx context.Context, key string) (bool // The document data is stored into result, the document meta data is returned. // If no document exists with given key, a NotFoundError is returned. func (c *vertexCollection) ReadDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, error) { - if err := validateKey(key); err != nil { + meta, _, err := c.readDocument(ctx, key, result) + if err != nil { return DocumentMeta{}, WithStack(err) } + return meta, nil +} + +func (c *vertexCollection) readDocument(ctx context.Context, key string, result interface{}) (DocumentMeta, contextSettings, error) { + if err := validateKey(key); err != nil { + return DocumentMeta{}, contextSettings{}, WithStack(err) + } escapedKey := pathEscape(key) req, err := c.conn.NewRequest("GET", path.Join(c.relPath(), escapedKey)) if err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } + cs := applyContextSettings(ctx, req) resp, err := c.conn.Do(ctx, req) if err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } if err := resp.CheckStatus(200); err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } // Parse metadata var meta DocumentMeta if err := resp.ParseBody("vertex", &meta); err != nil { - return DocumentMeta{}, WithStack(err) + return DocumentMeta{}, contextSettings{}, WithStack(err) } // Parse result if result != nil { if err := resp.ParseBody("vertex", result); err != nil { - return meta, WithStack(err) + return meta, contextSettings{}, WithStack(err) } } - return meta, nil + return meta, cs, nil +} + +// ReadDocuments reads multiple documents with given keys from the collection. +// The documents data is stored into elements of the given results slice, +// the documents meta data is returned. +// If no document exists with a given key, a NotFoundError is returned at its errors index. +func (c *vertexCollection) ReadDocuments(ctx context.Context, keys []string, results interface{}) (DocumentMetaSlice, ErrorSlice, error) { + resultsVal := reflect.ValueOf(results) + switch resultsVal.Kind() { + case reflect.Array, reflect.Slice: + // OK + default: + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("results data must be of kind Array, got %s", resultsVal.Kind())}) + } + if keys == nil { + return nil, nil, WithStack(InvalidArgumentError{Message: "keys nil"}) + } + resultCount := resultsVal.Len() + if len(keys) != resultCount { + return nil, nil, WithStack(InvalidArgumentError{Message: fmt.Sprintf("expected %d keys, got %d", resultCount, len(keys))}) + } + for _, key := range keys { + if err := validateKey(key); err != nil { + return nil, nil, WithStack(err) + } + } + metas := make(DocumentMetaSlice, resultCount) + errs := make(ErrorSlice, resultCount) + silent := false + for i := 0; i < resultCount; i++ { + result := resultsVal.Index(i).Addr() + ctx, err := withDocumentAt(ctx, i) + if err != nil { + return nil, nil, WithStack(err) + } + key := keys[i] + meta, cs, err := c.readDocument(ctx, key, result.Interface()) + if cs.Silent { + silent = true + } else { + metas[i], errs[i] = meta, err + } + } + if silent { + return nil, nil, nil + } + return metas, errs, nil } // CreateDocument creates a single document in the collection. diff --git a/deps/github.com/arangodb/go-driver/vst/authentication.go b/deps/github.com/arangodb/go-driver/vst/authentication.go index 97a66dab6..4a9d4de49 100644 --- a/deps/github.com/arangodb/go-driver/vst/authentication.go +++ b/deps/github.com/arangodb/go-driver/vst/authentication.go @@ -141,7 +141,7 @@ func (a *vstAuthenticationImpl) PrepareFunc(vstConn *vstConnection) func(ctx con // Wait for response m := <-respChan - resp, err := newResponse(m, "", nil) + resp, err := newResponse(m.Data, "", nil) if err != nil { return driver.WithStack(err) } diff --git a/deps/github.com/arangodb/go-driver/vst/connection.go b/deps/github.com/arangodb/go-driver/vst/connection.go index cb8254d37..456ca6b1e 100644 --- a/deps/github.com/arangodb/go-driver/vst/connection.go +++ b/deps/github.com/arangodb/go-driver/vst/connection.go @@ -183,7 +183,7 @@ func (c *vstConnection) do(ctx context.Context, req driver.Request, transport me } } - vstResp, err := newResponse(msg, c.endpoint.String(), rawResponse) + vstResp, err := newResponse(msg.Data, c.endpoint.String(), rawResponse) if err != nil { fmt.Printf("Cannot decode msg %d: %#v\n", msg.ID, err) return nil, driver.WithStack(err) diff --git a/deps/github.com/arangodb/go-driver/vst/protocol/connection.go b/deps/github.com/arangodb/go-driver/vst/protocol/connection.go index d1af33c4a..71b57853d 100644 --- a/deps/github.com/arangodb/go-driver/vst/protocol/connection.go +++ b/deps/github.com/arangodb/go-driver/vst/protocol/connection.go @@ -230,7 +230,7 @@ func (c *Connection) readChunkLoop() { if err != nil { if !c.IsClosed() { // Handle error - if err == io.EOF { + if driver.Cause(err) == io.EOF { // Connection closed c.Close() } else { @@ -286,5 +286,5 @@ func (c *Connection) updateLastActivity() { // IsIdle returns true when the last activity was more than the given timeout ago. func (c *Connection) IsIdle(idleTimeout time.Duration) bool { - return time.Since(c.lastActivity) > idleTimeout + return time.Since(c.lastActivity) > idleTimeout && c.msgStore.Size() == 0 } diff --git a/deps/github.com/arangodb/go-driver/vst/protocol/transport.go b/deps/github.com/arangodb/go-driver/vst/protocol/transport.go index 36192c889..105428187 100644 --- a/deps/github.com/arangodb/go-driver/vst/protocol/transport.go +++ b/deps/github.com/arangodb/go-driver/vst/protocol/transport.go @@ -228,7 +228,7 @@ func (c *Transport) createConnection() (*Connection, error) { func (c *Transport) cleanup() { for { time.Sleep(c.IdleConnTimeout / 10) - remaining, _ := c.CloseIdleConnections() + _, remaining := c.CloseIdleConnections() if remaining == 0 { return } diff --git a/deps/github.com/arangodb/go-driver/vst/response.go b/deps/github.com/arangodb/go-driver/vst/response.go index e290e6c12..2beb225b9 100644 --- a/deps/github.com/arangodb/go-driver/vst/response.go +++ b/deps/github.com/arangodb/go-driver/vst/response.go @@ -28,7 +28,6 @@ import ( "sync" driver "github.com/arangodb/go-driver" - "github.com/arangodb/go-driver/vst/protocol" velocypack "github.com/arangodb/go-velocypack" ) @@ -46,9 +45,9 @@ type vstResponse struct { } // newResponse builds a vstResponse from given message. -func newResponse(msg protocol.Message, endpoint string, rawResponse *[]byte) (*vstResponse, error) { +func newResponse(msgData []byte, endpoint string, rawResponse *[]byte) (*vstResponse, error) { // Decode header - hdr := velocypack.Slice(msg.Data) + hdr := velocypack.Slice(msgData) if err := hdr.AssertType(velocypack.Array); err != nil { return nil, driver.WithStack(err) }