Skip to content

Hotfix/external user delete sync error#551

Merged
smarcet merged 2 commits into
mainfrom
hotfix/external-user-delete-synch-error
May 27, 2026
Merged

Hotfix/external user delete sync error#551
smarcet merged 2 commits into
mainfrom
hotfix/external-user-delete-synch-error

Conversation

@smarcet
Copy link
Copy Markdown
Collaborator

@smarcet smarcet commented May 27, 2026

ref: https://app.clickup.com/t/86ba4rpw0

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved error handling in member account lifecycle operations to prevent unexpected crashes while maintaining system stability through logging.
    • Enhanced email conflict resolution when multiple accounts claim the same email address during external user synchronization.
  • Tests

    • Added test coverage for email reassignment scenarios in member registration workflows.

Review Change Stack

smarcet added 2 commits May 27, 2026 09:56
When the IDP deletes a user and reassigns its email to a different external
id, registration failed with a Member.Email unique-constraint violation. Free
the former member's email (the IDP is the source of truth) before assigning it.

- MemberService::registerExternalUserByPayload: invalidate the former member's
  email when the email moved to a new external id
- ResourceServerContext::getCurrentUser: same collision guard before setEmail
- ScheduleEntity: wrap lifecycle event/cache hooks so a queue/cache failure
  cannot roll back the Doctrine delete/update
- EventServiceProvider: dispatch ScheduleEntityLifeCycleEvent via
  JobDispatcher::withDbFallback
- add reproducing functional test (tests/MemberServiceTest.php)
…inst email collision

registerExternalUser created a Member with the IDP email without checking
whether a former member (e.g. one whose delete failed) still held it, hitting
the Member.Email unique constraint. Apply the same invalidate-former-email
guard already used in registerExternalUserByPayload.

Add a reproducing test for the create-branch collision (tests/MemberServiceTest.php).
@smarcet smarcet requested review from Copilot and romanetar May 27, 2026 14:00
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

Email collision handling is added to member registration and OAuth2 contexts to prevent duplicate email ownership. ScheduleEntity lifecycle hooks gain error resilience via try/catch wrapping. Event dispatching for schedule entity lifecycle changes is refactored to use JobDispatcher. Comprehensive tests validate email reassignment behavior.

Changes

Member and schedule entity resilience improvements

Layer / File(s) Summary
Member service email reassignment logic
app/Services/Model/Imp/MemberService.php
registerExternalUser and registerExternalUserByPayload detect email collisions by querying existing email holders and invalidating their emails with a placeholder before reassigning to the current member.
Member email reassignment test suite
tests/MemberServiceTest.php
Integration test cases set up conflicting member fixtures, execute registration flows within and outside transactions, verify correct email ownership post-reassignment, confirm former members' emails are invalidated, and provide fixture cleanup and helper builders.
OAuth2 context email collision guard
app/Models/OAuth2/ResourceServerContext.php
getCurrentUser now queries for email collisions during member email updates; if another member owns the email, logs a warning and invalidates their email before proceeding with reassignment.
ScheduleEntity lifecycle resilience
app/Models/Foundation/Summit/ScheduleEntity.php
Doctrine lifecycle hooks wrap event dispatch and cache operations in try/catch blocks, logging warnings on failure. updated() preserves early-exit logic and removes a stray semicolon.
Event dispatcher routing refactor
app/Providers/EventServiceProvider.php
ScheduleEntityLifeCycleEvent is routed through JobDispatcher::withDbFallback instead of direct dispatch, constructing a job with event metadata and logContext for tracing.

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • romanetar

Poem

🐰 A rabbit's ode to collision care:
When emails clash and members share,
We guard them well with placeholder mail,
And wrap the hooks so they don't fail—
The bouncing code now flows with care!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Hotfix/external user delete sync error' is directly related to the main objective: handling external user deletions that cause email sync errors by preventing unique-constraint violations when emails are reassigned.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hotfix/external-user-delete-synch-error

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

📘 OpenAPI / Swagger preview

➡️ https://OpenStackweb.github.io/summit-api/openapi/pr-551/

This page is automatically updated on each push to this PR.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hotfixes external-user email reassignment flows so stale local members do not block IDP-driven registration/update on the unique Member.Email constraint, and hardens schedule lifecycle notifications against queue/cache failures.

Changes:

  • Invalidates conflicting member emails before external user registration/update paths reuse an IDP-owned email.
  • Dispatches schedule lifecycle processing through JobDispatcher::withDbFallback.
  • Adds tests for member-service email reassignment collision scenarios.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
app/Services/Model/Imp/MemberService.php Frees conflicting emails during external user registration paths.
app/Models/OAuth2/ResourceServerContext.php Handles email collisions while syncing current user fields from auth context.
app/Models/Foundation/Summit/ScheduleEntity.php Makes schedule lifecycle dispatch/cache failures non-fatal.
app/Providers/EventServiceProvider.php Uses DB fallback dispatch for schedule lifecycle processing jobs.
tests/MemberServiceTest.php Adds regression coverage for external user email reassignment collisions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +260 to 262
$member_by_email->setEmail(sprintf("%s-invalid@invalid", $member_by_email->getId()));
}
$member->setEmail($user_email);
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
app/Providers/EventServiceProvider.php (1)

351-360: 💤 Low value

Consider using named parameters for consistency.

The existing JobDispatcher::withDbFallback calls in this file (lines 174-182) use named parameters (job:, logContext:), while this call uses positional arguments. Using named parameters here would improve consistency and readability.

♻️ Suggested refactor for consistency
-            JobDispatcher::withDbFallback(
-                new ProcessScheduleEntityLifeCycleEvent(
-                    $event->entity_operator,
-                    $event->summit_id,
-                    $event->entity_id,
-                    $event->entity_type,
-                    $event->params
-                ),
-                ['entity_type' => $event->entity_type, 'entity_id' => $event->entity_id]
+            JobDispatcher::withDbFallback(
+                job: new ProcessScheduleEntityLifeCycleEvent(
+                    $event->entity_operator,
+                    $event->summit_id,
+                    $event->entity_id,
+                    $event->entity_type,
+                    $event->params
+                ),
+                logContext: ['entity_type' => $event->entity_type, 'entity_id' => $event->entity_id]
             );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Providers/EventServiceProvider.php` around lines 351 - 360, The call to
JobDispatcher::withDbFallback uses positional args; change it to use named
parameters for consistency with other calls—pass the
ProcessScheduleEntityLifeCycleEvent instance as job: and the context array as
logContext: (i.e., call JobDispatcher::withDbFallback(job: new
ProcessScheduleEntityLifeCycleEvent(...), logContext: ['entity_type' => ...,
'entity_id' => ...])) so it matches the earlier calls and improves readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/Providers/EventServiceProvider.php`:
- Around line 351-360: The call to JobDispatcher::withDbFallback uses positional
args; change it to use named parameters for consistency with other calls—pass
the ProcessScheduleEntityLifeCycleEvent instance as job: and the context array
as logContext: (i.e., call JobDispatcher::withDbFallback(job: new
ProcessScheduleEntityLifeCycleEvent(...), logContext: ['entity_type' => ...,
'entity_id' => ...])) so it matches the earlier calls and improves readability.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 015e6345-c90e-4f86-9295-b6673214c612

📥 Commits

Reviewing files that changed from the base of the PR and between ce5c56b and fafcf59.

📒 Files selected for processing (5)
  • app/Models/Foundation/Summit/ScheduleEntity.php
  • app/Models/OAuth2/ResourceServerContext.php
  • app/Providers/EventServiceProvider.php
  • app/Services/Model/Imp/MemberService.php
  • tests/MemberServiceTest.php

Copy link
Copy Markdown
Collaborator

@romanetar romanetar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@smarcet smarcet merged commit bf8c4c2 into main May 27, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants