Resolve merge conflicts
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
"github.com/kjannette/koin-ping/backend/internal/domain"
|
||||
)
|
||||
@@ -37,10 +38,55 @@ func scanUser(row pgx.Row) (*domain.User, error) {
|
||||
return &u, nil
|
||||
}
|
||||
|
||||
func (m *UserModel) getByFirebaseUID(ctx context.Context, firebaseUID string) (*domain.User, error) {
|
||||
row := m.pool.QueryRow(ctx,
|
||||
`SELECT `+userColumns+` FROM users WHERE firebase_uid = $1`,
|
||||
firebaseUID,
|
||||
)
|
||||
return scanUser(row)
|
||||
}
|
||||
|
||||
func (m *UserModel) getByEmail(ctx context.Context, email string) (*domain.User, error) {
|
||||
row := m.pool.QueryRow(ctx,
|
||||
`SELECT `+userColumns+` FROM users WHERE email = $1`,
|
||||
email,
|
||||
)
|
||||
return scanUser(row)
|
||||
}
|
||||
|
||||
func isUniqueViolation(err error) bool {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
return pgErr.Code == "23505"
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FindOrCreateByFirebaseUID returns the local user for a Firebase UID,
|
||||
// creating one if it doesn't exist yet. On conflict (returning user) the
|
||||
// updated_at timestamp is refreshed.
|
||||
// creating one if it doesn't exist yet.
|
||||
//
|
||||
// For onboarding races or legacy duplicate-identity states, this method
|
||||
// gracefully falls back to an existing row by email instead of failing
|
||||
// with a unique-email violation.
|
||||
func (m *UserModel) FindOrCreateByFirebaseUID(ctx context.Context, firebaseUID, email string) (*domain.User, error) {
|
||||
existingByUID, err := m.getByFirebaseUID(ctx, firebaseUID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingByUID != nil {
|
||||
return existingByUID, nil
|
||||
}
|
||||
|
||||
if email != "" {
|
||||
existingByEmail, err := m.getByEmail(ctx, email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingByEmail != nil {
|
||||
return existingByEmail, nil
|
||||
}
|
||||
}
|
||||
|
||||
row := m.pool.QueryRow(ctx,
|
||||
`INSERT INTO users (firebase_uid, email)
|
||||
VALUES ($1, $2)
|
||||
@@ -48,7 +94,33 @@ func (m *UserModel) FindOrCreateByFirebaseUID(ctx context.Context, firebaseUID,
|
||||
RETURNING `+userColumns,
|
||||
firebaseUID, email,
|
||||
)
|
||||
return scanUser(row)
|
||||
user, err := scanUser(row)
|
||||
if err == nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// If a concurrent request inserted by email or firebase_uid first,
|
||||
// read the existing record and continue without surfacing a 500.
|
||||
if isUniqueViolation(err) {
|
||||
existingByUID, readErr := m.getByFirebaseUID(ctx, firebaseUID)
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
if existingByUID != nil {
|
||||
return existingByUID, nil
|
||||
}
|
||||
if email != "" {
|
||||
existingByEmail, readErr := m.getByEmail(ctx, email)
|
||||
if readErr != nil {
|
||||
return nil, readErr
|
||||
}
|
||||
if existingByEmail != nil {
|
||||
return existingByEmail, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (m *UserModel) GetByID(ctx context.Context, id string) (*domain.User, error) {
|
||||
|
||||
Reference in New Issue
Block a user