UpdateUserEmail updates a user's email and all related references.
(ctx context.Context, user *UserMessage, newEmail string)
| 483 | |
| 484 | // UpdateUserEmail updates a user's email and all related references. |
| 485 | func (s *Store) UpdateUserEmail(ctx context.Context, user *UserMessage, newEmail string) (*UserMessage, error) { |
| 486 | newEmail = strings.ToLower(newEmail) |
| 487 | |
| 488 | tx, err := s.GetDB().BeginTx(ctx, nil) |
| 489 | if err != nil { |
| 490 | return nil, err |
| 491 | } |
| 492 | defer tx.Rollback() |
| 493 | |
| 494 | // 1. Update Principal table |
| 495 | query := qb.Q().Space(`UPDATE principal SET email = ? WHERE id = ? |
| 496 | RETURNING id, deleted, email, name, password_hash, mfa_config, phone, profile, created_at`, |
| 497 | newEmail, user.ID) |
| 498 | sqlStr, args, err := query.ToSQL() |
| 499 | if err != nil { |
| 500 | return nil, errors.Wrapf(err, "failed to build update principal sql") |
| 501 | } |
| 502 | |
| 503 | updatedUser, err := scanPrincipalRow(ctx, tx, sqlStr, args) |
| 504 | if err != nil { |
| 505 | return nil, errors.Wrapf(err, "failed to update principal email") |
| 506 | } |
| 507 | |
| 508 | // 1b. Update creator/deleter columns that previously relied on ON UPDATE CASCADE. |
| 509 | creatorUpdates := []string{ |
| 510 | "UPDATE plan SET creator = $1 WHERE creator = $2", |
| 511 | "UPDATE task_run SET creator = $1 WHERE creator = $2", |
| 512 | "UPDATE issue SET creator = $1 WHERE creator = $2", |
| 513 | "UPDATE issue_comment SET creator = $1 WHERE creator = $2", |
| 514 | "UPDATE query_history SET creator = $1 WHERE creator = $2", |
| 515 | "UPDATE worksheet SET creator = $1 WHERE creator = $2", |
| 516 | "UPDATE worksheet_organizer SET principal = $1 WHERE principal = $2", |
| 517 | "UPDATE revision SET deleter = $1 WHERE deleter = $2", |
| 518 | "UPDATE release SET creator = $1 WHERE creator = $2", |
| 519 | "UPDATE access_grant SET creator = $1 WHERE creator = $2", |
| 520 | } |
| 521 | for _, stmt := range creatorUpdates { |
| 522 | if _, err := tx.ExecContext(ctx, stmt, newEmail, user.Email); err != nil { |
| 523 | return nil, errors.Wrapf(err, "failed to update creator/deleter references") |
| 524 | } |
| 525 | } |
| 526 | |
| 527 | // 2. Update RoleGrant in issue payload. |
| 528 | // The user in RoleGrant is stored as "users/{email}" within the JSON payload. |
| 529 | // We use text replacement for the specific path. |
| 530 | oldUserRef := common.FormatUserEmail(user.Email) |
| 531 | newUserRef := common.FormatUserEmail(newEmail) |
| 532 | |
| 533 | // 'roleGrant' is the json key for role_grant field in Issue proto. |
| 534 | query = qb.Q().Space(` |
| 535 | UPDATE issue |
| 536 | SET payload = jsonb_set(payload, '{roleGrant,user}', to_jsonb(?::text)) |
| 537 | WHERE payload->'roleGrant'->>'user' = ?`, |
| 538 | newUserRef, oldUserRef) |
| 539 | sqlStr, args, err = query.ToSQL() |
| 540 | if err != nil { |
| 541 | return nil, errors.Wrapf(err, "failed to build update role grant sql") |
| 542 | } |
no test coverage detected