From 5015e220696b375e466b19428ccc70bdca3bdb7d Mon Sep 17 00:00:00 2001 From: SebastianKrupinski Date: Fri, 16 Oct 2026 17:05:17 -0400 Subject: [PATCH] fix: delete availability when no time slots are defined Signed-off-by: SebastianKrupinski --- apps/dav/src/service/CalendarService.spec.ts | 48 ++++++++++++++++++++ apps/dav/src/service/CalendarService.ts | 18 ++++++++ 2 files changed, 66 insertions(+) create mode 100644 apps/dav/src/service/CalendarService.spec.ts diff --git a/apps/dav/src/service/CalendarService.spec.ts b/apps/dav/src/service/CalendarService.spec.ts new file mode 100644 index 0000000000000..f41f362771989 --- /dev/null +++ b/apps/dav/src/service/CalendarService.spec.ts @@ -0,0 +1,48 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { afterEach, describe, expect, it, vi } from 'vitest' +import { getEmptySlots, saveScheduleInboxAvailability } from './CalendarService.ts' + +const customRequest = vi.fn() +vi.mock('../dav/client.ts', () => ({ + getClient: () => ({ customRequest }), +})) + +afterEach(() => { + customRequest.mockReset() +}) + +describe('saveScheduleInboxAvailability', () => { + it('removes the availability property when there are no slots', async () => { + await saveScheduleInboxAvailability(getEmptySlots(), 'UTC') + + expect(customRequest).toHaveBeenCalledOnce() + const [path, { method, data }] = customRequest.mock.calls[0] + expect(path).toBe('inbox') + expect(method).toBe('PROPPATCH') + expect(data).toContain('') + expect(data).not.toContain('') + expect(data).not.toContain('VAVAILABILITY') + }) + + it('sets the availability property when there are slots', async () => { + const slots = getEmptySlots() + slots.MO = [{ + start: new Date('2026-10-12T09:00:00Z').getTime() / 1000, + end: new Date('2026-10-12T17:00:00Z').getTime() / 1000, + }] + + await saveScheduleInboxAvailability(slots, 'UTC') + + expect(customRequest).toHaveBeenCalledOnce() + const [path, { method, data }] = customRequest.mock.calls[0] + expect(path).toBe('inbox') + expect(method).toBe('PROPPATCH') + expect(data).toContain('') + expect(data).toContain('BEGIN:VAVAILABILITY') + expect(data).toContain('BEGIN:AVAILABLE') + }) +}) diff --git a/apps/dav/src/service/CalendarService.ts b/apps/dav/src/service/CalendarService.ts index 2a60e87f44cec..ef9bb7390911a 100644 --- a/apps/dav/src/service/CalendarService.ts +++ b/apps/dav/src/service/CalendarService.ts @@ -66,6 +66,24 @@ export async function saveScheduleInboxAvailability(slots, timezoneId) { day: dayId, })))] + // Leave availability unspecified rather than explicitly empty (which would be interpreted as explicitly unavailable RFC 7953) when slots are cleared. + if (all.length === 0) { + logger.debug('No availability slots, removing availability') + + await getClient().customRequest('inbox', { + method: 'PROPPATCH', + data: ` + + + + + + + `, + }) + return + } + const vavailability = slotsToVavailability(all, timezoneId) logger.debug('New availability ical created', {