fix(user,customer): return 409 on phone duplicate-key races

Map E11000 from the per-collection phone index to friendly conflict
errors and skip redundant GetByPhone when the phone is unchanged.

Not blocking: IAT test DB may need a one-off dedupe for index build;
phone uniqueness is per collection (users vs customers), not global.
This commit is contained in:
Mazyar
2026-06-06 20:34:45 +03:30
parent 0da0c8b8c9
commit 22487eaef5
7 changed files with 143 additions and 20 deletions
+44
View File
@@ -0,0 +1,44 @@
package mongo
import (
"errors"
"strings"
mongodriver "go.mongodb.org/mongo-driver/v2/mongo"
)
// IsDuplicateKeyError reports whether err is a MongoDB E11000 duplicate key error.
func IsDuplicateKeyError(err error) bool {
if err == nil {
return false
}
if strings.Contains(err.Error(), "E11000") || strings.Contains(err.Error(), "duplicate key") {
return true
}
var we mongodriver.WriteException
if errors.As(err, &we) {
for _, wErr := range we.WriteErrors {
if wErr.Code == 11000 {
return true
}
}
}
return false
}
// DuplicateKeyMatchesField reports whether a duplicate key error involves the given field
// (e.g. "phone" matches index phone_idx or dup key { phone: "..." }).
func DuplicateKeyMatchesField(err error, field string) bool {
if err == nil || field == "" {
return false
}
msg := strings.ToLower(err.Error())
field = strings.ToLower(field)
if strings.Contains(msg, field+"_idx") {
return true
}
if strings.Contains(msg, "dup key") && strings.Contains(msg, field) {
return true
}
return strings.Contains(msg, `"`+field+`"`) || strings.Contains(msg, field+":")
}