Skip to main content
Glama
xml-response-factory.ts7.22 kB
/** * Factory for creating XML response strings for testing */ import { Calendar, Event } from '../../models/calendar.js'; import { XmlService } from '../../services/xml/index.js'; // Create a singleton instance of XmlService for use in this factory const xmlService = new XmlService(); /** * Factory for creating XML responses for CalDAV operations */ export class XMLResponseFactory { /** * Create a PROPFIND response for calendars * @param options.calendars Calendars to include in the response * @param options.baseUrl Base URL for the server * @param options.username Username for paths * @returns XML string representing a PROPFIND response */ static createPropfindResponse(options: { calendars: Calendar[]; baseUrl?: string; username?: string; }): string { const baseUrl = options.baseUrl || 'https://nextcloud.example.com'; const username = options.username || 'testuser'; const caldavUrl = `${baseUrl}/remote.php/dav/calendars/${username}/`; let xmlResponse = `<?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns">`; // Add parent response (calendar home) xmlResponse += ` <d:response> <d:href>${caldavUrl}</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> </d:resourcetype> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response>`; // Add a response for each calendar for (const calendar of options.calendars) { const calendarUrl = `${caldavUrl}${calendar.id}/`; const isDefault = calendar.isDefault ? '1' : '0'; const supportedComponents = '<comp name="VEVENT"/><comp name="VTODO"/>'; xmlResponse += ` <d:response> <d:href>${calendarUrl}</d:href> <d:propstat> <d:prop> <d:resourcetype> <d:collection/> <cal:calendar/> </d:resourcetype> <d:displayname>${xmlService.escapeXml(calendar.displayName)}</d:displayname> <oc:calendar-enabled>1</oc:calendar-enabled> <cal:supported-calendar-component-set>${supportedComponents}</cal:supported-calendar-component-set> <oc:calendar-color>${xmlService.escapeXml(calendar.color)}</oc:calendar-color> <oc:owner-principal>principals/users/${xmlService.escapeXml(calendar.owner)}</oc:owner-principal> <cs:getctag>"${Date.now()}"</cs:getctag> <oc:is-default-calendar>${isDefault}</oc:is-default-calendar>`; if (calendar.category) { xmlResponse += ` <oc:calendar-category>${xmlService.escapeXml(calendar.category)}</oc:calendar-category>`; } if (calendar.focusPriority !== null && calendar.focusPriority !== undefined) { xmlResponse += ` <oc:calendar-focus-priority>${calendar.focusPriority}</oc:calendar-focus-priority>`; } xmlResponse += ` <cal:read>${calendar.permissions.canRead ? '1' : '0'}</cal:read> <cal:write>${calendar.permissions.canWrite ? '1' : '0'}</cal:write> <cal:share>${calendar.permissions.canShare ? '1' : '0'}</cal:share> <cal:delete>${calendar.permissions.canDelete ? '1' : '0'}</cal:delete> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response>`; } xmlResponse += ` </d:multistatus>`; return xmlResponse; } /** * Create a REPORT response for events * @param options.events Events to include in the response * @param options.baseUrl Base URL for the server * @param options.username Username for paths * @returns XML string representing a REPORT response */ static createEventsReportResponse(options: { events: Event[]; baseUrl?: string; username?: string; }): string { const baseUrl = options.baseUrl || 'https://nextcloud.example.com'; const username = options.username || 'testuser'; let xmlResponse = `<?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns">`; // Add a response for each event for (const event of options.events) { const caldavUrl = `${baseUrl}/remote.php/dav/calendars/${username}/`; const eventUrl = `${caldavUrl}${event.calendarId}/${event.id}.ics`; // Generate a simple iCalendar representation for testing const iCalData = this.createBasicICalForEvent(event); xmlResponse += ` <d:response> <d:href>${eventUrl}</d:href> <d:propstat> <d:prop> <d:getetag>"${Date.now()}"</d:getetag> <cal:calendar-data>${xmlService.escapeXml(iCalData)}</cal:calendar-data> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response>`; } xmlResponse += ` </d:multistatus>`; return xmlResponse; } /** * Create a basic iCalendar string for an event * @param event The event to convert to iCalendar format * @returns A basic iCalendar string */ private static createBasicICalForEvent(event: Event): string { const formatDate = (date: Date) => date .toISOString() .replace(/[-:]/g, '') .replace(/\.\d+Z$/, 'Z'); const startDate = formatDate(event.start); const endDate = formatDate(event.end); const createdDate = formatDate(event.created); const modifiedDate = formatDate(event.lastModified); return `BEGIN:VCALENDAR VERSION:2.0 PRODID:-//Nextcloud Calendar//EN CALSCALE:GREGORIAN BEGIN:VEVENT UID:${event.id} SUMMARY:${event.title} DESCRIPTION:${event.description || ''} DTSTART:${startDate} DTEND:${endDate} LOCATION:${event.location || ''} ORGANIZER:mailto:${event.organizer || ''} STATUS:${event.status || 'CONFIRMED'} CREATED:${createdDate} LAST-MODIFIED:${modifiedDate} END:VEVENT END:VCALENDAR`; } /** * Create a MKCALENDAR response (empty - just status code matters) * @returns Empty string (server returns empty body with 201 Created) */ static createMkcalendarResponse(): string { return ''; } /** * Create a PROPPATCH response (success) * @returns XML string representing a successful PROPPATCH response */ static createProppatchResponse(): string { return `<?xml version="1.0"?> <d:multistatus xmlns:d="DAV:" xmlns:cal="urn:ietf:params:xml:ns:caldav" xmlns:cs="http://calendarserver.org/ns/" xmlns:oc="http://owncloud.org/ns"> <d:response> <d:href>/remote.php/dav/calendars/testuser/test-calendar/</d:href> <d:propstat> <d:prop> <d:displayname /> <oc:calendar-color /> </d:prop> <d:status>HTTP/1.1 200 OK</d:status> </d:propstat> </d:response> </d:multistatus>`; } /** * Create an error response * @param status HTTP status code * @param message Error message * @returns XML string representing an error response */ static createErrorResponse(status: number, message: string): string { return `<?xml version="1.0"?> <d:error xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"> <s:message>${xmlService.escapeXml(message)}</s:message> <s:exception>Some\\Exception</s:exception> </d:error>`; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Cheffromspace/mcp-nextcloud-calendar'

If you have feedback or need assistance with the MCP directory API, please join our Discord server