From 03a8e5c932c39ab1c0c02f1b2757bfc27c9170cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kmie=C4=87?= Date: Fri, 18 Apr 2025 10:17:45 +0000 Subject: [PATCH 1/3] Conflict Error cannot be reproduced --- error.go | 9 ++++++ v2/arangodb/collection_documents_create.go | 16 +++++----- v2/arangodb/shared/response.go | 4 +++ ...atabase_collection_doc_create_code_test.go | 29 +++++++++++++++++-- 4 files changed, 48 insertions(+), 10 deletions(-) diff --git a/error.go b/error.go index 893e1ca6..3d577c74 100644 --- a/error.go +++ b/error.go @@ -421,6 +421,15 @@ func (ae ArangoError) Temporary() bool { return ae.HasError && ae.Code == http.StatusServiceUnavailable } +// GetConflictFieldname if error was caused by Conflict it returns the fieldname that caused it or "" otherwise +func (ae ArangoError) GetConflictFieldname() string { + if IsConflict(error(ae)) { + return ae.ErrorMessage + } else { + return "" + } +} + // newArangoError creates a new ArangoError with given values. func newArangoError(code, errorNum int, errorMessage string) error { return ArangoError{ diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index 846c99d9..b7558f91 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -49,21 +49,21 @@ type CollectionDocumentCreate interface { // CreateDocuments creates multiple documents in the collection. // The document data is loaded from the given documents slice, the documents metadata is returned. - // If a documents element already contains a `_key` field, this will be used as key of the new document, + // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. - // If a documents element contains a `_key` field with a duplicate key, other any other field violates an index constraint, - // a ConflictError is returned in its indeed in the errors slice. - // If the create request itself fails or one of the arguments is invalid, an error is returned. + // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, + // A ConflictError is returned if its indeed in the errors slice. + // If the created request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocuments(ctx context.Context, documents interface{}) (CollectionDocumentCreateResponseReader, error) // CreateDocumentsWithOptions creates multiple documents in the collection. // The document data is loaded from the given documents slice, the documents metadata is returned. - // If a documents element already contains a `_key` field, this will be used as key of the new document, + // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. - // If a documents element contains a `_key` field with a duplicate key, other any other field violates an index constraint, - // a ConflictError is returned in its indeed in the errors slice. - // If the create request itself fails or one of the arguments is invalid, an error is returned. + // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, + // A ConflictError is returned if its indeed in the errors slice. + // If the created request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocumentsWithOptions(ctx context.Context, documents interface{}, opts *CollectionDocumentCreateOptions) (CollectionDocumentCreateResponseReader, error) } diff --git a/v2/arangodb/shared/response.go b/v2/arangodb/shared/response.go index 00d77a59..249acb87 100644 --- a/v2/arangodb/shared/response.go +++ b/v2/arangodb/shared/response.go @@ -100,6 +100,10 @@ func (r ResponseStruct) AsArangoError() ArangoError { a.ErrorMessage = *r.ErrorMessage } + if r.ErrorMessage != nil { + a.ErrorMessage = *r.ErrorMessage + } + return a } diff --git a/v2/tests/database_collection_doc_create_code_test.go b/v2/tests/database_collection_doc_create_code_test.go index a59181ca..5121e6a5 100644 --- a/v2/tests/database_collection_doc_create_code_test.go +++ b/v2/tests/database_collection_doc_create_code_test.go @@ -25,6 +25,7 @@ import ( "testing" "github.com/arangodb/go-driver/v2/arangodb/shared" + "github.com/arangodb/go-driver/v2/utils" "github.com/stretchr/testify/require" @@ -62,16 +63,40 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { 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) { + + type DocWithCode struct { + Key string `json:"_key,omitempty"` + Code string `json:"code2"` + } + doc := DocWithCode{ - Key: "test", + Key: "test", + Code: "ww", } doc2 := DocWithCode{ - Key: "test2", + Key: "test2", + Code: "ww2", } _, err := col.CreateDocuments(ctx, []any{ doc, doc2, }) + + doc3 := DocWithCode{ + Key: "test", + Code: "ww3", + } + doc4 := DocWithCode{ + Key: "test2", + Code: "ww4", + } + + require.NoError(t, err) + _, err = col.CreateDocumentsWithOptions(ctx, []any{ + doc3, doc4, + }, &arangodb.CollectionDocumentCreateOptions{ + OverwriteMode: utils.NewType(arangodb.CollectionDocumentCreateOverwriteModeConflict), + }) require.NoError(t, err) docs, err := col.ReadDocuments(ctx, []string{ From e8222102d227481e408c51ebab5da2331a071ae3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kmie=C4=87?= Date: Fri, 18 Apr 2025 14:25:22 +0000 Subject: [PATCH 2/3] Added GetConflictKey to ArangoError. --- error.go | 9 ------ v2/arangodb/collection_documents_create.go | 4 +-- v2/arangodb/shared/error.go | 20 ++++++++++++- ...atabase_collection_doc_create_code_test.go | 29 ++----------------- .../database_collection_doc_create_test.go | 5 +++- 5 files changed, 27 insertions(+), 40 deletions(-) diff --git a/error.go b/error.go index 3d577c74..893e1ca6 100644 --- a/error.go +++ b/error.go @@ -421,15 +421,6 @@ func (ae ArangoError) Temporary() bool { return ae.HasError && ae.Code == http.StatusServiceUnavailable } -// GetConflictFieldname if error was caused by Conflict it returns the fieldname that caused it or "" otherwise -func (ae ArangoError) GetConflictFieldname() string { - if IsConflict(error(ae)) { - return ae.ErrorMessage - } else { - return "" - } -} - // newArangoError creates a new ArangoError with given values. func newArangoError(code, errorNum int, errorMessage string) error { return ArangoError{ diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index b7558f91..fd87de14 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -52,7 +52,7 @@ type CollectionDocumentCreate interface { // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, - // A ConflictError is returned if its indeed in the errors slice. + // then the ConflictError for the document can only be accessed by reading from CollectionDocumentCreateResponseReader and the error output of this function // If the created request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocuments(ctx context.Context, documents interface{}) (CollectionDocumentCreateResponseReader, error) @@ -62,7 +62,7 @@ type CollectionDocumentCreate interface { // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, - // A ConflictError is returned if its indeed in the errors slice. + // then the ConflictError for the document can only be accessed by reading from CollectionDocumentCreateResponseReader and the error output of this function // If the created request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocumentsWithOptions(ctx context.Context, documents interface{}, opts *CollectionDocumentCreateOptions) (CollectionDocumentCreateResponseReader, error) diff --git a/v2/arangodb/shared/error.go b/v2/arangodb/shared/error.go index f2aa97bf..a7fe0078 100644 --- a/v2/arangodb/shared/error.go +++ b/v2/arangodb/shared/error.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2017-2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2017-2025 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. @@ -26,6 +26,7 @@ import ( "fmt" "io" "net/http" + "regexp" ) const ( @@ -423,6 +424,23 @@ func (ae ArangoError) Temporary() bool { return ae.HasError && ae.Code == http.StatusServiceUnavailable } +// GetConflictKey if error was caused by Conflict it returns the key that caused it or "" otherwise +func (ae ArangoError) GetConflictKey() string { + if IsConflict(error(ae)) { + // Whitespace symbols are not allowed as part of a key so trimming them will result in a mistake. + re := regexp.MustCompile(`conflicting key:\s*(.+)$`) + match := re.FindStringSubmatch(ae.ErrorMessage) + if len(match) == 1 { + return match[1] + } else { + return "" + } + + } else { + return "" + } +} + // newArangoError creates a new ArangoError with given values. func newArangoError(code, errorNum int, errorMessage string) error { return ArangoError{ diff --git a/v2/tests/database_collection_doc_create_code_test.go b/v2/tests/database_collection_doc_create_code_test.go index 5121e6a5..a59181ca 100644 --- a/v2/tests/database_collection_doc_create_code_test.go +++ b/v2/tests/database_collection_doc_create_code_test.go @@ -25,7 +25,6 @@ import ( "testing" "github.com/arangodb/go-driver/v2/arangodb/shared" - "github.com/arangodb/go-driver/v2/utils" "github.com/stretchr/testify/require" @@ -63,40 +62,16 @@ func Test_DatabaseCollectionDocCreateCode(t *testing.T) { 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) { - - type DocWithCode struct { - Key string `json:"_key,omitempty"` - Code string `json:"code2"` - } - doc := DocWithCode{ - Key: "test", - Code: "ww", + Key: "test", } doc2 := DocWithCode{ - Key: "test2", - Code: "ww2", + Key: "test2", } _, err := col.CreateDocuments(ctx, []any{ doc, doc2, }) - - doc3 := DocWithCode{ - Key: "test", - Code: "ww3", - } - doc4 := DocWithCode{ - Key: "test2", - Code: "ww4", - } - - require.NoError(t, err) - _, err = col.CreateDocumentsWithOptions(ctx, []any{ - doc3, doc4, - }, &arangodb.CollectionDocumentCreateOptions{ - OverwriteMode: utils.NewType(arangodb.CollectionDocumentCreateOverwriteModeConflict), - }) require.NoError(t, err) docs, err := col.ReadDocuments(ctx, []string{ diff --git a/v2/tests/database_collection_doc_create_test.go b/v2/tests/database_collection_doc_create_test.go index 2c1d1a5f..9eaa181e 100644 --- a/v2/tests/database_collection_doc_create_test.go +++ b/v2/tests/database_collection_doc_create_test.go @@ -1,7 +1,7 @@ // // DISCLAIMER // -// Copyright 2023-2024 ArangoDB GmbH, Cologne, Germany +// Copyright 2023-2025 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. @@ -24,6 +24,7 @@ import ( "context" "testing" + "github.com/arangodb/go-driver/v2/arangodb/shared" "github.com/arangodb/go-driver/v2/utils" "github.com/stretchr/testify/require" @@ -77,6 +78,8 @@ func Test_DatabaseCollectionDocCreateOverwrite(t *testing.T) { OverwriteMode: overwriteMode.New(), }) require.Error(t, err) + + require.Equal(t, meta.Key, err.(shared.ArangoError).GetConflictKey()) require.Empty(t, metaConflict.Rev) require.Empty(t, metaConflict.Old) require.Empty(t, metaConflict.New) From d2c9e623aeae4a435c5f0abfb9251ba34161cf05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Kmie=C4=87?= Date: Tue, 22 Apr 2025 08:27:20 +0000 Subject: [PATCH 3/3] Improving comments --- v2/arangodb/collection_documents_create.go | 10 ++++++---- v2/arangodb/shared/error.go | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/v2/arangodb/collection_documents_create.go b/v2/arangodb/collection_documents_create.go index fd87de14..53d4c914 100644 --- a/v2/arangodb/collection_documents_create.go +++ b/v2/arangodb/collection_documents_create.go @@ -52,8 +52,9 @@ type CollectionDocumentCreate interface { // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, - // then the ConflictError for the document can only be accessed by reading from CollectionDocumentCreateResponseReader and the error output of this function - // If the created request itself fails or one of the arguments is invalid, an error is returned. + // then the ConflictError for a specific document will be returned only while reading from CollectionDocumentCreateResponseReader + // and not as the error output of this function. + // If the create request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocuments(ctx context.Context, documents interface{}) (CollectionDocumentCreateResponseReader, error) @@ -62,8 +63,9 @@ type CollectionDocumentCreate interface { // If a document element already contains a `_key` field, this will be used as key of the new document, // otherwise a unique key is created. // If a document element contains a `_key` field with a duplicate key, or any other field that violates an index constraint, - // then the ConflictError for the document can only be accessed by reading from CollectionDocumentCreateResponseReader and the error output of this function - // If the created request itself fails or one of the arguments is invalid, an error is returned. + // then the ConflictError for a specific document will be returned only while reading from CollectionDocumentCreateResponseReader + // and not as the error output of this function. + // If the create request itself fails or one of the arguments is invalid, an error is returned. // SmartGraphs and EnterpriseGraphs cannot use existing collections and cannot use the document interface CreateDocumentsWithOptions(ctx context.Context, documents interface{}, opts *CollectionDocumentCreateOptions) (CollectionDocumentCreateResponseReader, error) } diff --git a/v2/arangodb/shared/error.go b/v2/arangodb/shared/error.go index a7fe0078..b6477221 100644 --- a/v2/arangodb/shared/error.go +++ b/v2/arangodb/shared/error.go @@ -427,7 +427,7 @@ func (ae ArangoError) Temporary() bool { // GetConflictKey if error was caused by Conflict it returns the key that caused it or "" otherwise func (ae ArangoError) GetConflictKey() string { if IsConflict(error(ae)) { - // Whitespace symbols are not allowed as part of a key so trimming them will result in a mistake. + // Whitespace symbols are not allowed as part of a key so trimming them will not result in a mistake. re := regexp.MustCompile(`conflicting key:\s*(.+)$`) match := re.FindStringSubmatch(ae.ErrorMessage) if len(match) == 1 {