Skip to content

Fix/deduplicate username validation#29789

Open
Ashid332 wants to merge 4 commits into
calcom:mainfrom
Ashid332:fix/deduplicate-username-validation
Open

Fix/deduplicate username validation#29789
Ashid332 wants to merge 4 commits into
calcom:mainfrom
Ashid332:fix/deduplicate-username-validation

Conversation

@Ashid332

Copy link
Copy Markdown

What does this PR do?

  • Fixes #XXXX (GitHub issue number)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings
  • My PR is too large (>500 lines or >10 files) and should be split into smaller PRs

Ashid332 added 3 commits July 15, 2026 20:43
- Replaces flawed containment logic with strict interval intersection math
- Centralizes Prisma where-clause generation into prismaIntervalOverlap.ts
- Migrates getBusyTimes, BookingRepository, and HolidayRepository to new utility
- Adds comprehensive behavioral and unit test coverage for overlapping scenarios
fix: resolve busy time overlap logic and centralize interval queries
@github-actions

Copy link
Copy Markdown
Contributor

Welcome to Cal.diy, @Ashid332! Thanks for opening this pull request.

A few things to keep in mind:

  • This is Cal.diy, not Cal.com. Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will not be deployed to the Cal.com production app.
  • Please review our Contributing Guidelines if you haven't already.
  • Make sure your PR title follows the Conventional Commits format.

A maintainer will review your PR soon. Thanks for contributing!

@github-actions

Copy link
Copy Markdown
Contributor

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Fix/deduplicate username validation". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Ashid332, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 26 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 51fefcec-4f04-4ecc-98ea-78254c380318

📥 Commits

Reviewing files that changed from the base of the PR and between 5389542 and de65a67.

📒 Files selected for processing (1)
  • packages/lib/prismaIntervalOverlap.ts
📝 Walkthrough

Walkthrough

Adds a shared Prisma interval-overlap helper with strict and inclusive boundary modes, validation, and unit tests. Booking, busy-time, and holiday queries now use the helper for overlap filtering, with behavioral coverage for booking-limit intervals. Signup username checking now reuses the existing username check result and adjusts availability and suggestions for existing users who are not organization members.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The description is mostly a generic PR template and does not meaningfully describe the actual changes. Replace the template text with a short summary of the real changes and the main behavior it affects.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches one part of the changeset by describing the username validation refactor, though it misses the interval-overlap work.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/lib/server/username.ts (1)

234-261: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Ensure correct availability for org members claiming their own username and apply early returns.

By moving the base availability check to usernameCheck(usernameRaw), check.available will initially be false if the user's username is already set in the database (because usernameCheck will find them in the global namespace).

For a regular user (!userIsAMemberOfAnOrg), the code correctly overrides this to true when they claim their own username. However, if userIsAMemberOfAnOrg is true, the if (!userIsAMemberOfAnOrg) block is bypassed entirely, leaving check.available as false. This breaks the scenario where an org user claims their already-set username (which the test snippet expects to be true).

To resolve this regression, the isClaimingAlreadySetUsername check should be extracted so it applies to org members as well. Additionally, as per coding guidelines, we should use an early return (if (!user) return check;) to reduce nesting, and specify a select clause in the Prisma findFirst query for better performance.

🐛 Proposed fix and refactor
-  if (user) {
-    // TODO: When supporting multiple profiles of a user, we would need to check if the user has a membership with the correct organization
-    const userIsAMemberOfAnOrg = await prisma.membership.findFirst({
-      where: {
-        userId: user.id,
-        team: {
-          isOrganization: true,
-        },
-      },
-    });
-
-    // When we invite an email, that doesn't match the orgAutoAcceptEmail, we create a user with organizationId=null.
-    // The only way to differentiate b/w 'a new email that was invited to an Org' and 'a user that was created using regular signup' is to check if the user is a member of an org.
-    // If username is in global namespace
-    if (!userIsAMemberOfAnOrg) {
-      const isClaimingAlreadySetUsername = user.username === username;
-      const isClaimingUnsetUsername = !user.username;
-
-      if (!isClaimingUnsetUsername && !isClaimingAlreadySetUsername) {
-        check.available = false;
-      } else if (isClaimingAlreadySetUsername) {
-        check.available = true;
-        check.suggestedUsername = "";
-      }
-    }
-  }
-
-  return check;
+  if (!user) {
+    return check;
+  }
+
+  // TODO: When supporting multiple profiles of a user, we would need to check if the user has a membership with the correct organization
+  const userIsAMemberOfAnOrg = await prisma.membership.findFirst({
+    where: {
+      userId: user.id,
+      team: {
+        isOrganization: true,
+      },
+    },
+    select: {
+      id: true,
+    },
+  });
+
+  // When we invite an email, that doesn't match the orgAutoAcceptEmail, we create a user with organizationId=null.
+  // The only way to differentiate b/w 'a new email that was invited to an Org' and 'a user that was created using regular signup' is to check if the user is a member of an org.
+  const isClaimingAlreadySetUsername = user.username === username;
+  const isClaimingUnsetUsername = !user.username;
+
+  if (isClaimingAlreadySetUsername) {
+    check.available = true;
+    check.suggestedUsername = "";
+  } else if (!userIsAMemberOfAnOrg && !isClaimingUnsetUsername) {
+    check.available = false;
+  }
+
+  return check;
🤖 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 `@packages/lib/server/username.ts` around lines 234 - 261, Refactor the logic
around usernameCheck so it returns immediately when no user exists, and add a
minimal select clause to the membership.findFirst query. Compute
isClaimingAlreadySetUsername for every existing user, allowing check.available
and suggestedUsername to reflect ownership regardless of organization
membership; keep the unset-username and non-member restrictions within the
appropriate conditional.

Source: Coding guidelines

🧹 Nitpick comments (1)
packages/features/busyTimes/services/getBusyTimes.test.ts (1)

402-422: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace Day.js with native Date.

As per coding guidelines, use native Date instead of Day.js when timezone awareness isn't needed. The mock emulation and test parameters can be written simply with native Date methods, eliminating the overhead of Day.js object instantiation.

  • packages/features/busyTimes/services/getBusyTimes.test.ts#L402-L422: Use new Date(...) and native .getTime() comparisons instead of .isBefore()/.isAfter().
  • packages/features/busyTimes/services/getBusyTimes.test.ts#L461-L477: Use new Date(...) instead of dayjs(...).toDate(), and .toISOString() instead of .format().
🤖 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 `@packages/features/busyTimes/services/getBusyTimes.test.ts` around lines 402 -
422, Replace Day.js with native Date throughout the affected test setup in
packages/features/busyTimes/services/getBusyTimes.test.ts lines 402-422 and
461-477: use Date values for requestedStart/requestedEnd and booking
conversions, compare timestamps with getTime() in setupPrismaMock, and use
toISOString() where the existing code formats dates. No other behavior should
change.

Source: Coding guidelines

🤖 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.

Outside diff comments:
In `@packages/lib/server/username.ts`:
- Around line 234-261: Refactor the logic around usernameCheck so it returns
immediately when no user exists, and add a minimal select clause to the
membership.findFirst query. Compute isClaimingAlreadySetUsername for every
existing user, allowing check.available and suggestedUsername to reflect
ownership regardless of organization membership; keep the unset-username and
non-member restrictions within the appropriate conditional.

---

Nitpick comments:
In `@packages/features/busyTimes/services/getBusyTimes.test.ts`:
- Around line 402-422: Replace Day.js with native Date throughout the affected
test setup in packages/features/busyTimes/services/getBusyTimes.test.ts lines
402-422 and 461-477: use Date values for requestedStart/requestedEnd and booking
conversions, compare timestamps with getTime() in setupPrismaMock, and use
toISOString() where the existing code formats dates. No other behavior should
change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 62e277a5-15ee-496f-9454-c6dd114b04d2

📥 Commits

Reviewing files that changed from the base of the PR and between f004349 and 5389542.

📒 Files selected for processing (7)
  • packages/features/bookings/repositories/BookingRepository.ts
  • packages/features/busyTimes/services/getBusyTimes.test.ts
  • packages/features/busyTimes/services/getBusyTimes.ts
  • packages/features/holidays/repositories/HolidayRepository.ts
  • packages/lib/prismaIntervalOverlap.test.ts
  • packages/lib/prismaIntervalOverlap.ts
  • packages/lib/server/username.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant