From 6d8cbc3b39fd265ebdc608f9ba5d4c4fcbb7ec88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kmie=C4=87?= Date: Fri, 30 May 2025 07:57:43 +0000 Subject: [PATCH 1/2] Added ReadAll structure without support for OldObject --- test/on_failure.sh | 1 + v2/arangodb/collection_documents_create.go | 1 + .../collection_documents_create_impl.go | 2 + v2/arangodb/collection_documents_delete.go | 2 + .../collection_documents_delete_impl.go | 3 + v2/arangodb/shared/read_all.go | 79 +++++++++++ ...atabase_collection_doc_create_code_test.go | 129 +++++++++++------- 7 files changed, 171 insertions(+), 46 deletions(-) create mode 100644 v2/arangodb/shared/read_all.go diff --git a/test/on_failure.sh b/test/on_failure.sh index d43f1f62..26bffed0 100644 --- a/test/on_failure.sh +++ b/test/on_failure.sh @@ -1,4 +1,5 @@ #!/bin/bash +exit 1 echo "failure!\n" echo "\nARANGODB-STARTER logs: " diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index 53d4c914..052a87aa 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -71,6 +71,7 @@ type CollectionDocumentCreate interface { } type CollectionDocumentCreateResponseReader interface { + shared.ReadAllReadable[CollectionDocumentCreateResponse] Read() (CollectionDocumentCreateResponse, error) } diff --git a/v2/arangodb/collection_documents_create_impl.go b/v2/arangodb/collection_documents_create_impl.go index b8117243..5cbdc9c9 100644 --- a/v2/arangodb/collection_documents_create_impl.go +++ b/v2/arangodb/collection_documents_create_impl.go @@ -133,6 +133,7 @@ func newCollectionDocumentCreateResponseReader(array *connection.Array, options c.response.New = newUnmarshalInto(c.options.NewObject) } + c.ReadAllReader = shared.ReadAllReader[CollectionDocumentCreateResponse, *collectionDocumentCreateResponseReader]{Reader: c} return c } @@ -147,6 +148,7 @@ type collectionDocumentCreateResponseReader struct { Old *UnmarshalInto `json:"old,omitempty"` New *UnmarshalInto `json:"new,omitempty"` } + shared.ReadAllReader[CollectionDocumentCreateResponse, *collectionDocumentCreateResponseReader] } func (c *collectionDocumentCreateResponseReader) Read() (CollectionDocumentCreateResponse, error) { diff --git a/v2/arangodb/collection_documents_delete.go b/v2/arangodb/collection_documents_delete.go index 28641243..9475b094 100644 --- a/v2/arangodb/collection_documents_delete.go +++ b/v2/arangodb/collection_documents_delete.go @@ -63,6 +63,7 @@ type CollectionDocumentDeleteResponse struct { } type CollectionDocumentDeleteResponseReader interface { + shared.ReadAllIntoReadable[CollectionDocumentDeleteResponse] Read(i interface{}) (CollectionDocumentDeleteResponse, error) } @@ -82,6 +83,7 @@ type CollectionDocumentDeleteOptions struct { WithWaitForSync *bool // Return additionally the complete previous revision of the changed document + // Should be a pointer to an object OldObject interface{} // If set to true, an empty object is returned as response if the document operation succeeds. diff --git a/v2/arangodb/collection_documents_delete_impl.go b/v2/arangodb/collection_documents_delete_impl.go index 9a12837e..a92008c1 100644 --- a/v2/arangodb/collection_documents_delete_impl.go +++ b/v2/arangodb/collection_documents_delete_impl.go @@ -42,6 +42,7 @@ var _ CollectionDocumentDelete = &collectionDocumentDelete{} type collectionDocumentDelete struct { collection *collection + shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader] } func (c collectionDocumentDelete) DeleteDocument(ctx context.Context, key string) (CollectionDocumentDeleteResponse, error) { @@ -103,6 +104,7 @@ func (c collectionDocumentDelete) DeleteDocumentsWithOptions(ctx context.Context func newCollectionDocumentDeleteResponseReader(array *connection.Array, options *CollectionDocumentDeleteOptions) *collectionDocumentDeleteResponseReader { c := &collectionDocumentDeleteResponseReader{array: array, options: options} + c.ReadAllIntoReader = shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader]{Reader: c} return c } @@ -111,6 +113,7 @@ var _ CollectionDocumentDeleteResponseReader = &collectionDocumentDeleteResponse type collectionDocumentDeleteResponseReader struct { array *connection.Array options *CollectionDocumentDeleteOptions + shared.ReadAllIntoReader[CollectionDocumentDeleteResponse, *collectionDocumentDeleteResponseReader] } func (c *collectionDocumentDeleteResponseReader) Read(i interface{}) (CollectionDocumentDeleteResponse, error) { diff --git a/v2/arangodb/shared/read_all.go b/v2/arangodb/shared/read_all.go new file mode 100644 index 00000000..d6f7431d --- /dev/null +++ b/v2/arangodb/shared/read_all.go @@ -0,0 +1,79 @@ +package shared + +import ( + "errors" + "reflect" +) + +type readReader[T any] interface { + Read() (T, error) +} + +type ReadAllReadable[T any] interface { + ReadAll() ([]T, []error) +} + +type ReadAllReader[T any, R readReader[T]] struct { + Reader R +} + +func (r ReadAllReader[T, R]) ReadAll() ([]T, []error) { + var docs []T + var errs []error + for { + doc, e := r.Reader.Read() + if errors.Is(e, NoMoreDocumentsError{}) { + break + } + errs = append(errs, e) + docs = append(docs, doc) + } + return docs, errs +} + +type readReaderInto[T any] interface { + Read(i interface{}) (T, error) +} + +type ReadAllIntoReadable[T any] interface { + ReadAll(i interface{}) ([]T, []error) +} + +type ReadAllIntoReader[T any, R readReaderInto[T]] struct { + Reader R +} + +func (r ReadAllIntoReader[T, R]) ReadAll(i interface{}) ([]T, []error) { + + iVal := reflect.ValueOf(i) + if iVal.Kind() != reflect.Ptr || iVal.Elem().Kind() != reflect.Slice { + panic("i must be a pointer to a slice") + } + + eVal := iVal.Elem() + eType := eVal.Type().Elem() + + var docs []T + var errs []error + + for { + res := reflect.New(eType) + doc, e := r.Reader.Read(res.Interface()) + if errors.Is(e, NoMoreDocumentsError{}) { + break + } + + iDocVal := reflect.ValueOf(doc) + if iDocVal.Kind() == reflect.Ptr { + iDocVal = iDocVal.Elem() + } + docCopy := reflect.New(iDocVal.Type()).Elem() + docCopy.Set(iDocVal) + + errs = append(errs, e) + docs = append(docs, docCopy.Interface().(T)) + eVal = reflect.Append(eVal, res.Elem()) + } + iVal.Elem().Set(eVal) + return docs, errs +} diff --git a/v2/tests/database_collection_doc_create_code_test.go b/v2/tests/database_collection_doc_create_code_test.go index a59181ca..32904bc5 100644 --- a/v2/tests/database_collection_doc_create_code_test.go +++ b/v2/tests/database_collection_doc_create_code_test.go @@ -24,11 +24,10 @@ import ( "context" "testing" - "github.com/arangodb/go-driver/v2/arangodb/shared" - "github.com/stretchr/testify/require" "github.com/arangodb/go-driver/v2/arangodb" + "github.com/arangodb/go-driver/v2/arangodb/shared" ) type DocWithCode struct { @@ -38,66 +37,104 @@ type DocWithCode struct { func Test_DatabaseCollectionDocCreateCode(t *testing.T) { Wrap(t, func(t *testing.T, client arangodb.Client) { - WithDatabase(t, client, nil, func(db arangodb.Database) { - WithCollectionV2(t, db, nil, func(col arangodb.Collection) { - withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { - doc := DocWithCode{ - Key: "test", - } - - meta, err := col.CreateDocumentWithOptions(ctx, doc, &arangodb.CollectionDocumentCreateOptions{}) - require.NoError(t, err) - require.NotEmpty(t, meta.Rev) - require.Empty(t, meta.Old) - require.Empty(t, meta.New) - - rdoc, err := col.ReadDocument(ctx, "test", &doc) - require.NoError(t, err) - - require.EqualValues(t, "test", rdoc.Key) - }) - }) - }) + // WithDatabase(t, client, nil, func(db arangodb.Database) { + // WithCollectionV2(t, db, nil, func(col arangodb.Collection) { + // withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { + // doc := DocWithCode{ + // Key: "test", + // } + + // meta, err := col.CreateDocumentWithOptions(ctx, doc, &arangodb.CollectionDocumentCreateOptions{}) + // require.NoError(t, err) + // require.NotEmpty(t, meta.Rev) + // require.Empty(t, meta.Old) + // require.Empty(t, meta.New) + + // rdoc, err := col.ReadDocument(ctx, "test", &doc) + // require.NoError(t, err) + + // require.EqualValues(t, "test", rdoc.Key) + // }) + // }) + // }) + + // WithDatabase(t, client, nil, func(db arangodb.Database) { + // WithCollectionV2(t, db, nil, func(col arangodb.Collection) { + // withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { + // doc := DocWithCode{ + // Key: "test", + // } + // doc2 := DocWithCode{ + // Key: "test2", + // } + + // _, err := col.CreateDocuments(ctx, []any{ + // doc, doc2, + // }) + // require.NoError(t, err) + + // docs, err := col.ReadDocuments(ctx, []string{ + // "test", + // "tz44", + // "test2", + // }) + // require.NoError(t, err) + + // var z DocWithCode + + // meta, err := docs.Read(&z) + // require.NoError(t, err) + // require.Equal(t, "test", meta.Key) + + // _, err = docs.Read(&z) + // require.Error(t, err) + // require.True(t, shared.IsNotFound(err)) + + // meta, err = docs.Read(&z) + // require.NoError(t, err) + // require.Equal(t, "test2", meta.Key) + + // _, err = docs.Read(&z) + // require.Error(t, err) + // require.True(t, shared.IsNoMoreDocuments(err)) + // }) + // }) + // }) WithDatabase(t, client, nil, func(db arangodb.Database) { WithCollectionV2(t, db, nil, func(col arangodb.Collection) { withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { - doc := DocWithCode{ - Key: "test", + doc1 := DocWithCode{ + Key: "test", + Code: "code1", } doc2 := DocWithCode{ - Key: "test2", + Key: "test2", + Code: "code2", } - _, err := col.CreateDocuments(ctx, []any{ - doc, doc2, - }) + readerCrt, err := col.CreateDocuments(ctx, []any{doc1, doc2}) require.NoError(t, err) - docs, err := col.ReadDocuments(ctx, []string{ - "test", - "tz44", - "test2", - }) - require.NoError(t, err) + metaCrt, errs := readerCrt.ReadAll() - var z DocWithCode + require.ElementsMatch(t, []any{doc1.Key, doc2.Key}, []any{metaCrt[0].Key, metaCrt[1].Key}) + require.ElementsMatch(t, []any{nil, nil}, errs) - meta, err := docs.Read(&z) + var docRes DocWithCode + var docRead []DocWithCode + + readerDel, err := col.DeleteDocumentsWithOptions(ctx, []string{ + "test", "test2", "nonexistent", + }, &arangodb.CollectionDocumentDeleteOptions{OldObject: &docRes}) require.NoError(t, err) - require.EqualValues(t, "test", meta.Key) - _, err = docs.Read(&z) - require.Error(t, err) - require.True(t, shared.IsNotFound(err)) + metaDel, errs := readerDel.ReadAll(&docRead) - meta, err = docs.Read(&z) - require.NoError(t, err) - require.EqualValues(t, "test2", meta.Key) + require.ElementsMatch(t, []any{doc1.Key, doc2.Key, ""}, []any{metaDel[0].Key, metaDel[1].Key, metaDel[2].Key}) + require.ElementsMatch(t, []any{nil, nil, shared.ErrArangoDocumentNotFound}, []any{errs[0], errs[1], errs[2].(shared.ArangoError).ErrorNum}) + require.ElementsMatch(t, []any{doc1.Code, doc2.Code}, []any{metaDel[0].Old.(*DocWithCode).Code, metaDel[1].Old.(*DocWithCode).Code}) - _, err = docs.Read(&z) - require.Error(t, err) - require.True(t, shared.IsNoMoreDocuments(err)) }) }) }) From 804a880d2498b8348da0d69cc8c62d505cd22b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kmie=C4=87?= Date: Fri, 30 May 2025 08:53:55 +0000 Subject: [PATCH 2/2] Adding Documents Read --- v2/arangodb/collection_documents_read.go | 1 + v2/arangodb/collection_documents_read_impl.go | 3 ++- ...atabase_collection_doc_create_code_test.go | 23 +++++++++++++++---- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/v2/arangodb/collection_documents_read.go b/v2/arangodb/collection_documents_read.go index 32c49d83..7de82daf 100644 --- a/v2/arangodb/collection_documents_read.go +++ b/v2/arangodb/collection_documents_read.go @@ -59,6 +59,7 @@ type CollectionDocumentRead interface { type CollectionDocumentReadResponseReader interface { Read(i interface{}) (CollectionDocumentReadResponse, error) + shared.ReadAllIntoReadable[CollectionDocumentReadResponse] } type CollectionDocumentReadResponse struct { diff --git a/v2/arangodb/collection_documents_read_impl.go b/v2/arangodb/collection_documents_read_impl.go index 6a30bfb8..1ac405f0 100644 --- a/v2/arangodb/collection_documents_read_impl.go +++ b/v2/arangodb/collection_documents_read_impl.go @@ -97,7 +97,7 @@ func (c collectionDocumentRead) ReadDocumentWithOptions(ctx context.Context, key func newCollectionDocumentReadResponseReader(array *connection.Array, options *CollectionDocumentReadOptions) *collectionDocumentReadResponseReader { c := &collectionDocumentReadResponseReader{array: array, options: options} - + c.ReadAllIntoReader = shared.ReadAllIntoReader[CollectionDocumentReadResponse, *collectionDocumentReadResponseReader]{Reader: c} return c } @@ -106,6 +106,7 @@ var _ CollectionDocumentReadResponseReader = &collectionDocumentReadResponseRead type collectionDocumentReadResponseReader struct { array *connection.Array options *CollectionDocumentReadOptions + shared.ReadAllIntoReader[CollectionDocumentReadResponse, *collectionDocumentReadResponseReader] } func (c *collectionDocumentReadResponseReader) Read(i interface{}) (CollectionDocumentReadResponse, error) { diff --git a/v2/tests/database_collection_doc_create_code_test.go b/v2/tests/database_collection_doc_create_code_test.go index 32904bc5..37e1758d 100644 --- a/v2/tests/database_collection_doc_create_code_test.go +++ b/v2/tests/database_collection_doc_create_code_test.go @@ -37,6 +37,7 @@ type DocWithCode struct { func Test_DatabaseCollectionDocCreateCode(t *testing.T) { Wrap(t, func(t *testing.T, client arangodb.Client) { + // COMMENTED OUT FOR DEBUGGING ONLY // WithDatabase(t, client, nil, func(db arangodb.Database) { // WithCollectionV2(t, db, nil, func(col arangodb.Collection) { // withContextT(t, defaultTestTimeout, func(ctx context.Context, tb testing.TB) { @@ -121,18 +122,32 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { require.ElementsMatch(t, []any{doc1.Key, doc2.Key}, []any{metaCrt[0].Key, metaCrt[1].Key}) require.ElementsMatch(t, []any{nil, nil}, errs) - var docRes DocWithCode - var docRead []DocWithCode + var docRedRead []DocWithCode + + readeRed, err := col.ReadDocuments(ctx, []string{ + "test", "test2", "nonexistent", + }) + require.NoError(t, err) + + metaRed, errs := readeRed.ReadAll(&docRedRead) + + require.ElementsMatch(t, []any{doc1.Key, doc2.Key}, []any{metaRed[0].Key, metaRed[1].Key}) + require.ElementsMatch(t, []any{nil, nil, shared.ErrArangoDocumentNotFound}, []any{errs[0], errs[1], errs[2].(shared.ArangoError).ErrorNum}) + + var docOldObject DocWithCode + var docDelRead []DocWithCode readerDel, err := col.DeleteDocumentsWithOptions(ctx, []string{ "test", "test2", "nonexistent", - }, &arangodb.CollectionDocumentDeleteOptions{OldObject: &docRes}) + }, &arangodb.CollectionDocumentDeleteOptions{OldObject: &docOldObject}) require.NoError(t, err) - metaDel, errs := readerDel.ReadAll(&docRead) + metaDel, errs := readerDel.ReadAll(&docDelRead) require.ElementsMatch(t, []any{doc1.Key, doc2.Key, ""}, []any{metaDel[0].Key, metaDel[1].Key, metaDel[2].Key}) require.ElementsMatch(t, []any{nil, nil, shared.ErrArangoDocumentNotFound}, []any{errs[0], errs[1], errs[2].(shared.ArangoError).ErrorNum}) + + // Will fail as the OldObject is not supported by the ReadAll require.ElementsMatch(t, []any{doc1.Code, doc2.Code}, []any{metaDel[0].Old.(*DocWithCode).Code, metaDel[1].Old.(*DocWithCode).Code}) })