Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Multiple rr-hosts combine to create erroneous availability #18772

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

emrysal
Copy link
Contributor

@emrysal emrysal commented Jan 20, 2025

What does this PR do?

Fixes situation where overlapping date ranges from multiple rr hosts "merge" and result in a bigger sized slot than intended. Each date range needs to stay intact to generate the slot.

The slot logic also needed to be modified as now slots would be generated for each individual date range to prevent the following situation:

(host A 11:00-13:00, host B 11:15-13:00, frequency=45)

- 11:00
- 11:15
- 11:45
- 12:00

With new changes to the slot logic, the slots are consistent with previous behaviour:

- 11:00
- 11:45

Copy link
Contributor

github-actions bot commented Jan 20, 2025

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:

Unknown release type "bugfix" found in pull request title "bugfix: Multiple rr-hosts combine to create erroneous availability". 

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

@keithwillcode keithwillcode added core area: core, team members only foundation labels Jan 20, 2025
@emrysal emrysal changed the title wip: Open draft PR with passing test (should fail) bugfix: Multiple rr-hosts combine to create erroneous availability Jan 20, 2025
Copy link

vercel bot commented Jan 21, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
cal-com-ui-playground ❌ Failed (Inspect) Jan 30, 2025 5:50pm
2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Visit Preview Jan 30, 2025 5:50pm
calcom-web-canary ⬜️ Ignored (Inspect) Visit Preview Jan 30, 2025 5:50pm

@emrysal emrysal changed the title bugfix: Multiple rr-hosts combine to create erroneous availability fix: Multiple rr-hosts combine to create erroneous availability Jan 21, 2025
expect(result).toHaveLength(6);
});

it("can merge multiple time slot groups when multiple date ranges are given that overlap", async () => {
Copy link
Contributor Author

@emrysal emrysal Jan 22, 2025

Choose a reason for hiding this comment

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

Overlapping time slots weren't possible before as date ranges were merged, even rr hosts. Now they are possible because different rr hosts may have overlapping date ranges but we still want to handle individually.

@@ -233,12 +237,12 @@ function buildSlotsWithDateRanges({
};
}

slots.push(slotData);
slots.set(slotData.time.toISOString(), slotData);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Key based on start time (same as rendering in Booker)

},
// 12:00-12:20,12:20-12:40
{
start: nextDay.hour(11).minute(20),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Before this resulted in 7 slots, due to 11:20/11:40 overlap with dateRanges[0]. This is now corrected to the correct (5)

Copy link

graphite-app bot commented Jan 22, 2025

Graphite Automations

"Add foundation team as reviewer" took an action on this PR • (01/22/25)

1 reviewer was added to this PR based on Keith Williams's automation.

@emrysal emrysal added the high-risk Requires approval by Foundation team label Jan 22, 2025
@emrysal emrysal requested a review from CarinaWolli January 22, 2025 10:46

describe("getAggregatedAvailability", () => {
// rr-host availability used to combine into erroneous slots, this confirms it no longer happens
it("should have no host available between 11:00 and 11:30 on January 23, 2025", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test is failing on live code

return startDiff !== 0 ? startDiff : a.end.valueOf() - b.end.valueOf();
})
.filter((range) => {
const key = range.start.valueOf() * 1e12 + range.end.valueOf();
Copy link
Contributor Author

@emrysal emrysal Jan 24, 2025

Choose a reason for hiding this comment

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

This line generates a unique key for each date range by combining the start and end timestamps. Multiplying the start timestamp by 1e12 ensures that the start and end values are distinct and non-overlapping when combined, allowing the function to accurately identify and filter out duplicate date ranges.

Copy link
Member

Choose a reason for hiding this comment

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

This is not working correctly for me.

I have the following data:

Date range 1:
start: 2025-01-27T14:00:00Z
end: 2025-01-27T04:30:00-05:00

Date range 2:
start: 2025-01-27T14:00:00Z
end: 2025-01-27T14:45:00Z

Even though the end time is different, Date Range 2 is considered a duplicate and not returned

Copy link
Member

@CarinaWolli CarinaWolli left a comment

Choose a reason for hiding this comment

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

I found a potential bug when testing. Don't know yet for sure, but requesting changes to avoid merging until I know more

@github-actions github-actions bot marked this pull request as draft January 24, 2025 17:56
@CarinaWolli CarinaWolli dismissed their stale review January 24, 2025 18:54

Resolved, not sure why but was a local issue that slots weren't shown. But they were returned correctly

@CarinaWolli CarinaWolli marked this pull request as ready for review January 24, 2025 18:54
return startDiff !== 0 ? startDiff : a.end.valueOf() - b.end.valueOf();
})
.filter((range) => {
const key = range.start.valueOf() * 1e12 + range.end.valueOf();
Copy link
Member

Choose a reason for hiding this comment

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

This is not working correctly for me.

I have the following data:

Date range 1:
start: 2025-01-27T14:00:00Z
end: 2025-01-27T04:30:00-05:00

Date range 2:
start: 2025-01-27T14:00:00Z
end: 2025-01-27T14:45:00Z

Even though the end time is different, Date Range 2 is considered a duplicate and not returned

@github-actions github-actions bot marked this pull request as draft January 24, 2025 19:34
Copy link
Member

@CarinaWolli CarinaWolli left a comment

Choose a reason for hiding this comment

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

There are several issues in the adjustDateRanges function. I left some comments

You can reproduce the bug with this setup:
RR Host 1: 11:30 - 14:00
RR Host 2: 9:15 - 11:00

First day that's available has 11:30 blocked. All other days have 11:30 open


for (let i = 1; i < dateRanges.length; i++) {
// we skip if the date-range to adjust is outside the base date range day. - this avoids offset on other days.
if (dateRanges[i].start <= baseStart.startOf("day") || dateRanges[i].start >= baseEnd.endOf("day")) {
Copy link
Member

Choose a reason for hiding this comment

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

Does this work with startOf endOf day? Is this start of day in UTC? Or how will this exactly work?

const adjustDateRanges = (dateRanges: DateRange[], frequency: number) => {
if (dateRanges.length === 0) return;

const baseStart = dateRanges[0].start.clone(); // Reference start time
Copy link
Member

Choose a reason for hiding this comment

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

baseStart is the start of the dateRange, but not the start of the first slot. baseStart can be 9:15 but first 30 min slot will be at 9:30. This causes issues in the code below and we are losing slots

const baseEnd = dateRanges[0].end.clone();

for (let i = 1; i < dateRanges.length; i++) {
// we skip if the date-range to adjust is outside the base date range day. - this avoids offset on other days.
Copy link
Member

Choose a reason for hiding this comment

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

base date range day is the first day in date ranges. However dateranges are from the whole month. So this only handles the first day

slotStartTime =
slotStartTime.minute() % interval !== 0
? slotStartTime.startOf("hour").add(Math.ceil(slotStartTime.minute() / interval) * interval, "minute")
: slotStartTime;

// Adding 1 minute to date ranges that end at midnight to ensure that the last slot is included
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing organizerTimeZone, the way it was passed was highly buggy as we couldn't be sure if it was the right timeZone at this point in time. Instead moving it to date-ranges, where I'll round up to the interval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bookings area: bookings, availability, timezones, double booking 🐛 bug Something isn't working core area: core, team members only foundation high-risk Requires approval by Foundation team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants