From 9e699c9d1adcc5778d7cf3f2b0a24ba9e6ca6596 Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Fri, 15 Nov 2024 15:24:46 -0700 Subject: [PATCH 1/4] GODRIVER-3086 Add ErrorCodes to ServerError API --- mongo/errors.go | 84 +++++++++++++++++++++++++++++++++++--------- mongo/errors_test.go | 64 +++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 16 deletions(-) diff --git a/mongo/errors.go b/mongo/errors.go index b9f8ec8d8e..77f8769c4e 100644 --- a/mongo/errors.go +++ b/mongo/errors.go @@ -245,16 +245,33 @@ type LabeledError interface { // interface should not be used in production. type ServerError interface { LabeledError + // HasErrorCode returns true if the error has the specified code. HasErrorCode(int) bool + // HasErrorMessage returns true if the error contains the specified message. HasErrorMessage(string) bool + // HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message. HasErrorCodeWithMessage(int, string) bool + // ErrorCodes returns a deduplicated list of error codes returned by the + // server. + ErrorCodes() []int + serverError() } +func hasErrorCode(srvErr ServerError, code int) bool { + for _, srvErrCode := range srvErr.ErrorCodes() { + if code == srvErrCode { + return true + } + } + + return false +} + var _ ServerError = CommandError{} var _ ServerError = WriteError{} var _ ServerError = WriteException{} @@ -285,7 +302,13 @@ func (e CommandError) Unwrap() error { // HasErrorCode returns true if the error has the specified code. func (e CommandError) HasErrorCode(code int) bool { - return int(e.Code) == code + return hasErrorCode(e, code) +} + +// ErrorCodes returns a list of error codes returned by the server. +func (e CommandError) ErrorCodes() []int { + fmt.Println(e.Code) + return []int{int(e.Code)} } // HasErrorLabel returns true if the error contains the specified label. @@ -340,7 +363,12 @@ func (we WriteError) Error() string { // HasErrorCode returns true if the error has the specified code. func (we WriteError) HasErrorCode(code int) bool { - return we.Code == code + return hasErrorCode(we, code) +} + +// ErrorCodes returns a list of error codes returned by the server. +func (we WriteError) ErrorCodes() []int { + return []int{we.Code} } // HasErrorLabel returns true if the error contains the specified label. WriteErrors do not contain labels, @@ -449,15 +477,27 @@ func (mwe WriteException) Error() string { // HasErrorCode returns true if the error has the specified code. func (mwe WriteException) HasErrorCode(code int) bool { - if mwe.WriteConcernError != nil && mwe.WriteConcernError.Code == code { - return true + return hasErrorCode(mwe, code) +} + +// ErrorCodes returns a list of error codes returned by the server. +func (mwe WriteException) ErrorCodes() []int { + errorCodeSet := make(map[int]struct{}) + for _, writeError := range mwe.WriteErrors { + errorCodeSet[writeError.Code] = struct{}{} } - for _, we := range mwe.WriteErrors { - if we.Code == code { - return true - } + + if mwe.WriteConcernError != nil { + errorCodeSet[mwe.WriteConcernError.Code] = struct{}{} } - return false + + // Deduplicate error codes. + errorCodes := make([]int, 0, len(errorCodeSet)) + for code := range errorCodeSet { + errorCodes = append(errorCodes, code) + } + + return errorCodes } // HasErrorLabel returns true if the error contains the specified label. @@ -561,15 +601,27 @@ func (bwe BulkWriteException) Error() string { // HasErrorCode returns true if any of the errors have the specified code. func (bwe BulkWriteException) HasErrorCode(code int) bool { - if bwe.WriteConcernError != nil && bwe.WriteConcernError.Code == code { - return true + return hasErrorCode(bwe, code) +} + +// ErrorCodes returns a list of error codes returned by the server. +func (bwe BulkWriteException) ErrorCodes() []int { + errorCodeSet := make(map[int]struct{}) + for _, writeError := range bwe.WriteErrors { + errorCodeSet[writeError.Code] = struct{}{} } - for _, we := range bwe.WriteErrors { - if we.Code == code { - return true - } + + if bwe.WriteConcernError != nil { + errorCodeSet[bwe.WriteConcernError.Code] = struct{}{} } - return false + + // Deduplicate error codes. + errorCodes := make([]int, 0, len(errorCodeSet)) + for code := range errorCodeSet { + errorCodes = append(errorCodes, code) + } + + return errorCodes } // HasErrorLabel returns true if the error contains the specified label. diff --git a/mongo/errors_test.go b/mongo/errors_test.go index c39d409ac5..7164bae4bb 100644 --- a/mongo/errors_test.go +++ b/mongo/errors_test.go @@ -679,6 +679,70 @@ func TestIsTimeout(t *testing.T) { } } +func TestServerError_ErrorCodes(t *testing.T) { + tests := []struct { + name string + error ServerError + want []int + }{ + { + name: "CommandError", + error: CommandError{Code: 1}, + want: []int{1}, + }, + { + name: "WriteError", + error: WriteError{Code: 1}, + want: []int{1}, + }, + { + name: "WriteException single", + error: WriteException{WriteErrors: []WriteError{{Code: 1}}}, + want: []int{1}, + }, + { + name: "WriteException multiple", + error: WriteException{WriteErrors: []WriteError{{Code: 1}, {Code: 2}}}, + want: []int{1, 2}, + }, + { + name: "WriteException duplicates", + error: WriteException{WriteErrors: []WriteError{{Code: 1}, {Code: 2}, {Code: 2}}}, + want: []int{1, 2}, + }, + { + name: "BulkWriteException single", + error: BulkWriteException{WriteErrors: []BulkWriteError{{WriteError: WriteError{Code: 1}}}}, + want: []int{1}, + }, + { + name: "BulkWriteException multiple", + error: BulkWriteException{WriteErrors: []BulkWriteError{ + {WriteError: WriteError{Code: 1}}, + {WriteError: WriteError{Code: 2}}, + }}, + want: []int{1, 2}, + }, + { + name: "BulkWriteException duplicates", + error: BulkWriteException{WriteErrors: []BulkWriteError{ + {WriteError: WriteError{Code: 1}}, + {WriteError: WriteError{Code: 2}}, + {WriteError: WriteError{Code: 2}}, + }}, + want: []int{1, 2}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.error.ErrorCodes() + + assert.ElementsMatch(t, got, test.want) + }) + } +} + type netErr struct { timeout bool } From 382b4167d6e834e9a108ca13a9dae529fdfcd87b Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Fri, 15 Nov 2024 15:32:51 -0700 Subject: [PATCH 2/4] GODRIVER-3086 Remove empty lines and debug stmt --- mongo/errors.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mongo/errors.go b/mongo/errors.go index 77f8769c4e..79c92789a3 100644 --- a/mongo/errors.go +++ b/mongo/errors.go @@ -245,13 +245,10 @@ type LabeledError interface { // interface should not be used in production. type ServerError interface { LabeledError - // HasErrorCode returns true if the error has the specified code. HasErrorCode(int) bool - // HasErrorMessage returns true if the error contains the specified message. HasErrorMessage(string) bool - // HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message. HasErrorCodeWithMessage(int, string) bool @@ -307,7 +304,6 @@ func (e CommandError) HasErrorCode(code int) bool { // ErrorCodes returns a list of error codes returned by the server. func (e CommandError) ErrorCodes() []int { - fmt.Println(e.Code) return []int{int(e.Code)} } From 36f60b7956cdcba3b85d3672db892feca6fad04c Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Thu, 16 Jan 2025 22:47:20 -0700 Subject: [PATCH 3/4] GODRIVER-3086 Revert single code checks and no deduplication --- mongo/errors.go | 28 ++++++++-------------------- mongo/errors_test.go | 4 ++-- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/mongo/errors.go b/mongo/errors.go index 79c92789a3..142b1a52b0 100644 --- a/mongo/errors.go +++ b/mongo/errors.go @@ -299,7 +299,7 @@ func (e CommandError) Unwrap() error { // HasErrorCode returns true if the error has the specified code. func (e CommandError) HasErrorCode(code int) bool { - return hasErrorCode(e, code) + return int(e.Code) == code } // ErrorCodes returns a list of error codes returned by the server. @@ -359,7 +359,7 @@ func (we WriteError) Error() string { // HasErrorCode returns true if the error has the specified code. func (we WriteError) HasErrorCode(code int) bool { - return hasErrorCode(we, code) + return we.Code == code } // ErrorCodes returns a list of error codes returned by the server. @@ -478,19 +478,13 @@ func (mwe WriteException) HasErrorCode(code int) bool { // ErrorCodes returns a list of error codes returned by the server. func (mwe WriteException) ErrorCodes() []int { - errorCodeSet := make(map[int]struct{}) + errorCodes := []int{} for _, writeError := range mwe.WriteErrors { - errorCodeSet[writeError.Code] = struct{}{} + errorCodes = append(errorCodes, writeError.Code) } if mwe.WriteConcernError != nil { - errorCodeSet[mwe.WriteConcernError.Code] = struct{}{} - } - - // Deduplicate error codes. - errorCodes := make([]int, 0, len(errorCodeSet)) - for code := range errorCodeSet { - errorCodes = append(errorCodes, code) + errorCodes = append(errorCodes, mwe.WriteConcernError.Code) } return errorCodes @@ -602,19 +596,13 @@ func (bwe BulkWriteException) HasErrorCode(code int) bool { // ErrorCodes returns a list of error codes returned by the server. func (bwe BulkWriteException) ErrorCodes() []int { - errorCodeSet := make(map[int]struct{}) + errorCodes := []int{} for _, writeError := range bwe.WriteErrors { - errorCodeSet[writeError.Code] = struct{}{} + errorCodes = append(errorCodes, writeError.Code) } if bwe.WriteConcernError != nil { - errorCodeSet[bwe.WriteConcernError.Code] = struct{}{} - } - - // Deduplicate error codes. - errorCodes := make([]int, 0, len(errorCodeSet)) - for code := range errorCodeSet { - errorCodes = append(errorCodes, code) + errorCodes = append(errorCodes, bwe.WriteConcernError.Code) } return errorCodes diff --git a/mongo/errors_test.go b/mongo/errors_test.go index 7164bae4bb..2ff04c4dd2 100644 --- a/mongo/errors_test.go +++ b/mongo/errors_test.go @@ -708,7 +708,7 @@ func TestServerError_ErrorCodes(t *testing.T) { { name: "WriteException duplicates", error: WriteException{WriteErrors: []WriteError{{Code: 1}, {Code: 2}, {Code: 2}}}, - want: []int{1, 2}, + want: []int{1, 2, 2}, }, { name: "BulkWriteException single", @@ -730,7 +730,7 @@ func TestServerError_ErrorCodes(t *testing.T) { {WriteError: WriteError{Code: 2}}, {WriteError: WriteError{Code: 2}}, }}, - want: []int{1, 2}, + want: []int{1, 2, 2}, }, } From 1abe21bdf2b5d1957146f5a20c23e96a58253e56 Mon Sep 17 00:00:00 2001 From: Preston Vasquez Date: Wed, 29 Jan 2025 13:09:38 -0700 Subject: [PATCH 4/4] GODRIVER-3086 Update ErrorCodes documentation --- mongo/errors.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mongo/errors.go b/mongo/errors.go index 142b1a52b0..fc7667bb31 100644 --- a/mongo/errors.go +++ b/mongo/errors.go @@ -252,8 +252,10 @@ type ServerError interface { // HasErrorCodeWithMessage returns true if any of the contained errors have the specified code and message. HasErrorCodeWithMessage(int, string) bool - // ErrorCodes returns a deduplicated list of error codes returned by the - // server. + // ErrorCodes returns all error codes (unsorted) in the server’s response. + // This would include nested errors (e.g., write concern errors) for + // supporting implementations (e.g., BulkWriteException) as well as the + // top-level error code. ErrorCodes() []int serverError()