@coozzy/cal-dav
Advanced tools
Comparing version 2.4.5 to 2.4.6
@@ -5,13 +5,13 @@ import * as ICAL from 'ical.js'; | ||
import { Attendee, Alarm } from './lib/types'; | ||
import { EventWithExceptions } from 'ical.js'; | ||
import { EventFrequencyCalendarService } from '../types/calendar'; | ||
export interface CalDavClient { | ||
getEventByUrl(eventUrl: string): Promise<ICAL.Event | undefined>; | ||
getEventByUid(eventUid: string): Promise<ICAL.Event | undefined>; | ||
getEventOccurrencesByUrl(eventUrl: string, startDate: Date, endDate: Date): Promise<ICAL.EventOccurrence[] | undefined>; | ||
getEventsByEventUid(eventUid: string, startDate?: Date, endDate?: Date): Promise<ICAL.EventOccurrence[] | undefined>; | ||
deleteEvent(eventUid: string): Promise<void>; | ||
listAllEvents(): Promise<ICAL.Event[]>; | ||
createEvent(eventUrl: string, id: string, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, frequencyInterval?: string, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): Promise<void>; | ||
updateEvent(eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, exceptionEventId?: number, frequencyInterval?: string, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): Promise<void>; | ||
deleteOccurrenceEvent(eventUrl: string, event: ICAL.Event, exceptionEventId: number, prodid: string): Promise<void>; | ||
listEventsInTimeRange(startDate: Date, endDate?: Date): Promise<ICAL.EventWithExceptions[]>; | ||
updateEvent(eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, occurrenceEventId?: number, frequencyInterval?: string, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): Promise<void>; | ||
deleteOccurrenceEvent(eventUrl: string, event: ICAL.Event, occurrenceEventId: number, prodid: string): Promise<void>; | ||
listEventsInTimeRange(startDate: Date, endDate?: Date): Promise<ICAL.EventOccurrence[]>; | ||
multiGetEvents(eventUrls: string[]): Promise<ICAL.Event[]>; | ||
@@ -25,11 +25,13 @@ createEventWithString(event: string, eventUid: string): Promise<void>; | ||
getEventByUrl: (eventUrl: string) => Promise<ICAL.Event | undefined>; | ||
getEventByUid: (eventUid: string) => Promise<ICAL.Event | undefined>; | ||
getEventOccurrencesByUrl: (eventUrl: string, startDate: Date, endDate: Date) => Promise<ICAL.EventOccurrence[] | undefined>; | ||
getEventsByEventUid: (eventUid: string, startDate?: Date, endDate?: Date) => Promise<ICAL.EventOccurrence[] | undefined>; | ||
deleteEvent: (eventUrl: string) => Promise<void>; | ||
listAllEvents: () => Promise<ICAL.Event[]>; | ||
listEventsInTimeRange: (startDate: Date, endDate?: Date) => Promise<ICAL.EventWithExceptions[]>; | ||
listEventsInTimeRange: (startDate: Date, endDate?: Date) => Promise<ICAL.EventOccurrence[]>; | ||
multiGetEvents: (eventUrls: string[]) => Promise<ICAL.Event[]>; | ||
createEvent: (eventUrl: string, id: string, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]) => Promise<void>; | ||
updateEvent: (eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, exceptionEventId: number | undefined, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]) => Promise<void>; | ||
updateEvent: (eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, occurrenceEventId: number | undefined, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]) => Promise<void>; | ||
private getDateWithoutTime; | ||
updateEventProperties: (eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]) => string; | ||
deleteOccurrenceEvent: (eventUrl: string, event: ICAL.Event, exceptionEventId: number, prodid: string) => Promise<void>; | ||
deleteOccurrenceEvent: (eventUrl: string, event: ICAL.Event, occurrenceEventId: number, prodid: string) => Promise<void>; | ||
createEventWithString: (event: string, eventUid: string) => Promise<void>; | ||
@@ -36,0 +38,0 @@ static parseEvent: (eventData: string) => ICAL.Event; |
@@ -37,19 +37,35 @@ "use strict"; | ||
}; | ||
this.getEventByUid = async eventUid => { | ||
this.getEventOccurrencesByUrl = async (eventUrl, startDate, endDate) => { | ||
try { | ||
const response = await this.service.multiGetEvents([eventUrl]); | ||
if (response.status === 207) { | ||
_logger.default.info('CalDavClient.GetEventOccurrencesByUrl: Successfully got event and parsing all occurrences.'); | ||
return this.parseListOfEvents(response.data, 'getEventOccurrencesByUrl', startDate, endDate); | ||
} | ||
} catch (e) { | ||
throw new Error(`CalDavClient.GetEventOccurrencesByUrl: ${e.message}. `); | ||
} | ||
}; | ||
this.getEventsByEventUid = async (eventUid, startDate, endDate) => { | ||
try { | ||
const response = await this.service.getEventByUid(eventUid); | ||
if (response.status === 207) { | ||
const parsedData = await this.parser.parseEvent(response.data); | ||
const calData = ICAL.parse(parsedData.event); | ||
const comp = new ICAL.Component(calData); | ||
const vevent = comp.getFirstSubcomponent('vevent'); | ||
const event = new ICAL.Event(vevent); | ||
const urlParts = parsedData.url.split('/'); | ||
event.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
this.extendEventWithRecurrenceDetails(event); | ||
_logger.default.info(`CalDavClient.GetEvent: Successfully got event ${event.uid}. `); | ||
return event; | ||
if (startDate && endDate) { | ||
_logger.default.info('CalDavClient.getEventsByEventUid: Successfully got event and parsing all occurrences.'); | ||
return this.parseListOfEvents(response.data, 'getEventsByEventUid', startDate, endDate); | ||
} else { | ||
const parsedData = await this.parser.parseEvent(response.data); | ||
const calData = ICAL.parse(parsedData.event); | ||
const comp = new ICAL.Component(calData); | ||
const vevent = comp.getFirstSubcomponent('vevent'); | ||
const event = new ICAL.Event(vevent); | ||
const urlParts = parsedData.url.split('/'); | ||
event.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
this.extendEventWithRecurrenceDetails(event); | ||
_logger.default.info(`CalDavClient.getEventsByEventUid: Successfully got event ${event.uid}. `); | ||
return [event]; | ||
} | ||
} | ||
} catch (e) { | ||
throw new Error(`CalDavClient.GetEventByUid: ${e.message}. `); | ||
throw new Error(`CalDavClient.getEventsByEventUid: ${e.message}. `); | ||
} | ||
@@ -178,3 +194,3 @@ }; | ||
}; | ||
this.updateEvent = async (eventUrl, event, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, exceptionEventId, frequencyInterval, endDateFrequency, countFrequency, alarms) => { | ||
this.updateEvent = async (eventUrl, event, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, occurrenceEventId, frequencyInterval, endDateFrequency, countFrequency, alarms) => { | ||
try { | ||
@@ -193,3 +209,3 @@ _logger.default.debug(`Method updateEvent executed for eventUrl: ${eventUrl}`, { | ||
privateEvent, | ||
exceptionEventId, | ||
occurrenceEventId, | ||
frequencyInterval, | ||
@@ -202,5 +218,9 @@ endDateFrequency, | ||
let exceptionEventString = ''; | ||
if (exceptionEventId) { | ||
if (occurrenceEventId) { | ||
eventString = event.toString(); | ||
let exceptionEventIdIteration = 1; | ||
const exDates = event.component.getAllProperties('exdate'); | ||
const exDatesValues = []; | ||
for (const exDate of exDates) { | ||
exDatesValues.push(new Date(exDate.getFirstValue().toString()).getTime()); | ||
} | ||
let exceptions = {}; | ||
@@ -210,13 +230,82 @@ if (event.exceptions) { | ||
} | ||
for (const exception in exceptions) { | ||
exceptionEventString += '\n'; | ||
if (exceptionEventIdIteration === exceptionEventId) { | ||
_logger.default.debug(`Execute method updateEventProperties for exceptionEventId: ${exceptionEventId}`); | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += this.updateEventProperties(eventUrl, exceptionEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms); | ||
} else { | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += exceptionEvent.toString(); | ||
let exceptionProcessed = 0; | ||
let previousOccurrenceDate; | ||
let occurrenceEventIdIteration = 0; | ||
const iterator = event.iterator(); | ||
for (let next = iterator.next(); next; next = iterator.next()) { | ||
if (exceptionProcessed === exceptions.length && occurrenceEventIdIteration > occurrenceEventId) { | ||
break; | ||
} | ||
exceptionEventIdIteration += 1; | ||
occurrenceEventIdIteration += 1; | ||
if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
throw new Error('CalDavClient.UpdateEvent: Deleted occurrence can\'t be updated.'); | ||
} | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
continue; | ||
} | ||
let currentOccurrenceDate; | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
exceptionProcessed += 1; | ||
exceptionEventString += '\r\n'; | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
_logger.default.debug(`Execute method updateEventProperties for occurrenceEventId: ${occurrenceEventId}`); | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
currentOccurrenceDate = new ICAL.Time(startDate); | ||
// const recurrenceId = new ICAL.Time(next.toJSON()); | ||
// icalEvent.component.addPropertyWithValue('RECURRENCE-ID', recurrenceId); | ||
exceptionEventString += this.updateEventProperties(eventUrl, exceptionEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms, new ICAL.Time(next.toJSON())); | ||
} else { | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
currentOccurrenceDate = new ICAL.Time(exceptionEvent.startDate.toJSON()); | ||
exceptionEventString += exceptionEvent.toString(); | ||
} | ||
break; | ||
} | ||
} | ||
// process not deleted and not existing exception occurrence - create new exception | ||
if (occurrenceEventIdIteration === occurrenceEventId && !exceptionAdded) { | ||
const calDataOccurrence = ICAL.parse(`BEGIN:VCALENDAR\r\n${eventString}\r\nEND:VCALENDAR`); | ||
const compOccurrence = new ICAL.Component(calDataOccurrence); | ||
const veventOccurrence = compOccurrence.getFirstSubcomponent('vevent'); | ||
const icalEvent = new ICAL.Event(veventOccurrence); | ||
exceptionEventString += '\r\n'; | ||
currentOccurrenceDate = new ICAL.Time(startDate); | ||
exceptionEventString += this.updateEventProperties(eventUrl, icalEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms, new ICAL.Time(next.toJSON())); | ||
} | ||
if (!currentOccurrenceDate) { | ||
currentOccurrenceDate = next; | ||
} | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
if (previousOccurrenceDate && currentOccurrenceDate) { | ||
const currentOccurrenceDateTime = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
if (previousOccurrenceDate === currentOccurrenceDateTime) { | ||
throw new Error('Two occurrences cannot occur on the same day.'); | ||
} | ||
if (previousOccurrenceDate > currentOccurrenceDateTime) { | ||
throw new Error('Cannot reschedule an occurrence of the recurring appointment if it skips over a later occurrence of the same appointment.'); | ||
} | ||
} | ||
} else if (occurrenceEventIdIteration === occurrenceEventId + 1) { | ||
if (previousOccurrenceDate && currentOccurrenceDate) { | ||
const currentOccurrenceDateTime = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
if (previousOccurrenceDate === currentOccurrenceDateTime) { | ||
throw new Error('Two occurrences cannot occur on the same day!'); | ||
} | ||
if (previousOccurrenceDate > currentOccurrenceDateTime) { | ||
throw new Error('Cannot reschedule an occurrence of the recurring appointment if it skips over a later occurrence of the same appointment!'); | ||
} | ||
} | ||
} | ||
if (currentOccurrenceDate) { | ||
previousOccurrenceDate = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
} | ||
} | ||
@@ -232,3 +321,3 @@ } else { | ||
}; | ||
this.updateEventProperties = (eventUrl, event, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, frequencyInterval, endDateFrequency, countFrequency, alarms) => { | ||
this.updateEventProperties = (eventUrl, event, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, frequencyInterval, endDateFrequency, countFrequency, alarms, recurrenceId) => { | ||
try { | ||
@@ -252,2 +341,3 @@ _logger.default.debug(`Method updateEventProperties executed for eventUrl: ${eventUrl}`, { | ||
}); | ||
const tempId = event.uid; | ||
const originalSubcomponents = event.component.getAllSubcomponents(); | ||
@@ -271,6 +361,7 @@ const originalProperties = event.component.getAllProperties(); | ||
event.component = newEventComponent; | ||
event.uid = tempId; | ||
event.component.removeAllProperties('categories'); | ||
event.component.removeAllProperties('attendee'); | ||
event.component.removeAllProperties('class'); | ||
if (privateEvent) { | ||
event.component.removeAllProperties('class'); | ||
event.component.addPropertyWithValue('CLASS', 'PRIVATE'); | ||
@@ -293,3 +384,10 @@ } | ||
this.addFrequency(event, frequencyInterval, endDateFrequency, countFrequency); | ||
} else { | ||
event.component.removeAllProperties('rrule'); | ||
} | ||
if (recurrenceId) { | ||
recurrenceId.convertToZone(ICAL.Timezone.utcTimezone); | ||
event.component.removeAllProperties('recurrence-id'); | ||
event.component.addPropertyWithValue('RECURRENCE-ID', recurrenceId.toICALString()); | ||
} | ||
event.summary = title; | ||
@@ -326,21 +424,68 @@ event.description = description; | ||
}; | ||
this.deleteOccurrenceEvent = async (eventUrl, event, exceptionEventId, prodid) => { | ||
this.deleteOccurrenceEvent = async (eventUrl, event, occurrenceEventId, prodid) => { | ||
let eventString; | ||
try { | ||
let exceptions = {}; | ||
if (event.exceptions) { | ||
exceptions = event.exceptions; | ||
} | ||
let exceptionEventString = ''; | ||
let exceptionEventIdIteration = 1; | ||
for (const exception in exceptions) { | ||
if (exceptionEventIdIteration === exceptionEventId) { | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(exception.toString()), true)); | ||
} else { | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += '\n'; | ||
exceptionEventString += exceptionEvent.toString(); | ||
let newExceptionAdded = false; | ||
if (occurrenceEventId) { | ||
const iterator = event.iterator(); | ||
let occurrenceEventIdIteration = 0; | ||
const exDates = event.component.getAllProperties('exdate'); | ||
const exDatesValues = []; | ||
for (const exDate of exDates) { | ||
exDatesValues.push(new Date(exDate.getFirstValue().toString()).getTime()); | ||
} | ||
exceptionEventIdIteration += 1; | ||
let exceptions = {}; | ||
if (event.exceptions) { | ||
exceptions = event.exceptions; | ||
} | ||
let exceptionProcessed = 0; | ||
for (let next = iterator.next(); next; next = iterator.next()) { | ||
if (exceptionProcessed === exceptions.length && occurrenceEventIdIteration > occurrenceEventId) { | ||
break; | ||
} | ||
occurrenceEventIdIteration += 1; | ||
if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
throw new Error('CalDavClient.UpdateEvent: Occurrence has been already deleted!'); | ||
} | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
continue; | ||
} | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
exceptionProcessed += 1; | ||
exceptionEventString += '\r\n'; | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
newExceptionAdded = true; | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(exception.toString()), true)); | ||
} else { | ||
const exceptionEvent = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += exceptionEvent.toString(); | ||
} | ||
break; | ||
} | ||
} | ||
// process not deleted and not existing exception occurrence - create new exception | ||
if (occurrenceEventIdIteration === occurrenceEventId && !exceptionAdded) { | ||
newExceptionAdded = true; | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
} | ||
} | ||
if (occurrenceEventIdIteration === 1) { | ||
await this.deleteEvent(eventUrl); | ||
return; | ||
} | ||
} | ||
let eventString = event.toString(); | ||
if (!newExceptionAdded) { | ||
throw new Error(`CalDavClient.UpdateEvent: There is no occurrenceEventId: ${occurrenceEventId}`); | ||
} | ||
eventString = event.toString(); | ||
eventString += exceptionEventString; | ||
@@ -386,3 +531,3 @@ await this.service.createUpdateEvent(`BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:${prodid}\r\n` + eventString + '\r\nEND:VCALENDAR', eventUrl); | ||
} | ||
let exceptionEventId = 1; | ||
let occurrenceEventId = 0; | ||
const iterator = iCalEvent.iterator(); | ||
@@ -395,2 +540,3 @@ const calDataOccurrence = ICAL.parse(eventData.event); | ||
} | ||
occurrenceEventId += 1; | ||
if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
@@ -400,16 +546,22 @@ // occurrence deleted | ||
} | ||
let isException = false; | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
if (new Date(exception.toString()).getTime() === new Date(next.toString()).getTime()) { | ||
isException = true; | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
// exception is at midnight, we need to add time | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
const event = new ICAL.Event(exceptions[exception.toString()].component); | ||
if (this.eventIncludedInDates(event.startDate, event.endDate, startDateFilter, endDateFilter)) { | ||
event.exceptionEventId = exceptionEventId; | ||
exceptionEventId += 1; | ||
event.occurrenceEventId = occurrenceEventId; | ||
this.extendEventWithRecurrenceDetails(event); | ||
events.push(event); | ||
} | ||
break; | ||
} | ||
} | ||
if (!isException) { | ||
if (!exceptionAdded) { | ||
// process not deleted and not exception occurrence | ||
const compOccurrence = new ICAL.Component(calDataOccurrence); | ||
@@ -423,2 +575,3 @@ const veventOccurrence = compOccurrence.getFirstSubcomponent('vevent'); | ||
if (this.eventIncludedInDates(icalEvent.startDate, icalEvent.endDate, startDateFilter, endDateFilter)) { | ||
icalEvent.occurrenceEventId = occurrenceEventId; | ||
icalEvent.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
@@ -439,2 +592,5 @@ this.extendEventWithRecurrenceDetails(icalEvent); | ||
} | ||
getDateWithoutTime(date) { | ||
return new Date(date.toDateString()); | ||
} | ||
extendEventWithRecurrenceDetails(iCalEvent) { | ||
@@ -441,0 +597,0 @@ const rrule = iCalEvent.component.getFirstPropertyValue('rrule'); |
{ | ||
"name": "@coozzy/cal-dav", | ||
"version": "2.4.5", | ||
"version": "2.4.6", | ||
"description": "Simple cal dav client.", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
311
src/index.ts
@@ -6,3 +6,3 @@ import * as ICAL from 'ical.js'; | ||
import logger from '@coozzy/logger'; | ||
import {EventWithExceptions, FrequencyValues, TimeJsonData} from 'ical.js'; | ||
import {EventOccurrence, FrequencyValues, Time, TimeJsonData} from 'ical.js'; | ||
import {EventFrequencyCalendarService} from '../types/calendar'; | ||
@@ -14,4 +14,6 @@ | ||
getEventByUid(eventUid: string): Promise<ICAL.Event | undefined>; | ||
getEventOccurrencesByUrl(eventUrl: string, startDate: Date, endDate: Date): Promise<ICAL.EventOccurrence[] | undefined> | ||
getEventsByEventUid(eventUid: string, startDate?: Date, endDate?: Date): Promise<ICAL.EventOccurrence[] | undefined>; | ||
deleteEvent(eventUid: string): Promise<void>; | ||
@@ -50,3 +52,3 @@ | ||
privateEvent: boolean, | ||
exceptionEventId?: number, | ||
occurrenceEventId?: number, | ||
frequencyInterval?: string, | ||
@@ -59,6 +61,6 @@ endDateFrequency?: ICAL.TimeJsonData, | ||
event: ICAL.Event, | ||
exceptionEventId: number, | ||
occurrenceEventId: number, | ||
prodid: string): Promise<void>; | ||
listEventsInTimeRange(startDate: Date, endDate?: Date): Promise<ICAL.EventWithExceptions[]>; | ||
listEventsInTimeRange(startDate: Date, endDate?: Date): Promise<ICAL.EventOccurrence[]>; | ||
@@ -99,20 +101,38 @@ multiGetEvents(eventUrls: string[]): Promise<ICAL.Event[]>; | ||
getEventByUid = async(eventUid: string): Promise<ICAL.Event | undefined> => { | ||
getEventOccurrencesByUrl = async(eventUrl: string, startDate: Date, endDate: Date): Promise<ICAL.EventOccurrence[] | undefined> => { | ||
try{ | ||
const response = await this.service.multiGetEvents([eventUrl]); | ||
if(response.status === 207) { | ||
logger.info('CalDavClient.GetEventOccurrencesByUrl: Successfully got event and parsing all occurrences.'); | ||
return this.parseListOfEvents(response.data, 'getEventOccurrencesByUrl', startDate, endDate); | ||
} | ||
} catch(e) { | ||
throw new Error(`CalDavClient.GetEventOccurrencesByUrl: ${e.message}. `); | ||
} | ||
}; | ||
getEventsByEventUid = async(eventUid: string, startDate?: Date, endDate?: Date): Promise<ICAL.EventOccurrence[] | undefined> => { | ||
try{ | ||
const response = await this.service.getEventByUid(eventUid); | ||
if(response.status === 207) { | ||
const parsedData = await this.parser.parseEvent(response.data); | ||
const calData = ICAL.parse(parsedData.event); | ||
const comp = new ICAL.Component(calData); | ||
const vevent = comp.getFirstSubcomponent('vevent'); | ||
if (startDate && endDate) { | ||
logger.info('CalDavClient.getEventsByEventUid: Successfully got event and parsing all occurrences.'); | ||
return this.parseListOfEvents(response.data, 'getEventsByEventUid', startDate, endDate); | ||
} else { | ||
const parsedData = await this.parser.parseEvent(response.data); | ||
const calData = ICAL.parse(parsedData.event); | ||
const comp = new ICAL.Component(calData); | ||
const vevent = comp.getFirstSubcomponent('vevent'); | ||
const event = new ICAL.Event(vevent); | ||
const urlParts = parsedData.url.split('/'); | ||
event.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
this.extendEventWithRecurrenceDetails(event); | ||
logger.info(`CalDavClient.GetEvent: Successfully got event ${event.uid}. `); | ||
return event; | ||
const event = new ICAL.Event(vevent); | ||
const urlParts = parsedData.url.split('/'); | ||
event.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
this.extendEventWithRecurrenceDetails(event); | ||
logger.info(`CalDavClient.getEventsByEventUid: Successfully got event ${event.uid}. `); | ||
return [event]; | ||
} | ||
} | ||
} catch(e) { | ||
throw new Error(`CalDavClient.GetEventByUid: ${e.message}. `); | ||
throw new Error(`CalDavClient.getEventsByEventUid: ${e.message}. `); | ||
} | ||
@@ -147,3 +167,3 @@ }; | ||
listEventsInTimeRange = async(startDate: Date, endDate?: Date): Promise<ICAL.EventWithExceptions[]> => { | ||
listEventsInTimeRange = async(startDate: Date, endDate?: Date): Promise<ICAL.EventOccurrence[]> => { | ||
try{ | ||
@@ -261,3 +281,3 @@ const response = await this.service.listEventsInTimeRange(startDate, endDate); | ||
updateEvent = async(eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, exceptionEventId: number | undefined, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): Promise<void> => { | ||
updateEvent = async(eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, occurrenceEventId: number | undefined, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): Promise<void> => { | ||
try{ | ||
@@ -277,3 +297,3 @@ logger.debug(`Method updateEvent executed for eventUrl: ${eventUrl}`, | ||
privateEvent, | ||
exceptionEventId, | ||
occurrenceEventId, | ||
frequencyInterval, | ||
@@ -286,5 +306,11 @@ endDateFrequency, | ||
let exceptionEventString = ''; | ||
if (exceptionEventId) { | ||
if (occurrenceEventId) { | ||
eventString = event.toString(); | ||
let exceptionEventIdIteration = 1; | ||
const exDates = event.component.getAllProperties('exdate'); | ||
const exDatesValues: number[] = []; | ||
for (const exDate of exDates) { | ||
exDatesValues.push(new Date(exDate.getFirstValue().toString()).getTime()); | ||
} | ||
let exceptions: any = {}; | ||
@@ -294,13 +320,89 @@ if (event.exceptions) { | ||
} | ||
for (const exception in exceptions) { | ||
exceptionEventString += '\n'; | ||
if (exceptionEventIdIteration === exceptionEventId) { | ||
logger.debug(`Execute method updateEventProperties for exceptionEventId: ${exceptionEventId}`); | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += this.updateEventProperties(eventUrl, exceptionEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms); | ||
} else { | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += exceptionEvent.toString(); | ||
let exceptionProcessed = 0; | ||
let previousOccurrenceDate: number | undefined; | ||
let occurrenceEventIdIteration = 0; | ||
const iterator = event.iterator(); | ||
for (let next = iterator.next(); next; next = iterator.next()) { | ||
if (exceptionProcessed === exceptions.length && occurrenceEventIdIteration > occurrenceEventId) { | ||
break; | ||
} | ||
exceptionEventIdIteration += 1; | ||
occurrenceEventIdIteration += 1; | ||
if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
throw new Error('CalDavClient.UpdateEvent: Deleted occurrence can\'t be updated.'); | ||
} | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
continue; | ||
} | ||
let currentOccurrenceDate: ICAL.Time | undefined; | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
exceptionProcessed += 1; | ||
exceptionEventString += '\r\n'; | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
logger.debug(`Execute method updateEventProperties for occurrenceEventId: ${occurrenceEventId}`); | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
currentOccurrenceDate = new ICAL.Time(startDate); | ||
// const recurrenceId = new ICAL.Time(next.toJSON()); | ||
// icalEvent.component.addPropertyWithValue('RECURRENCE-ID', recurrenceId); | ||
exceptionEventString += this.updateEventProperties(eventUrl, exceptionEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms, new ICAL.Time(next.toJSON())); | ||
} else { | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
currentOccurrenceDate = new ICAL.Time(exceptionEvent.startDate.toJSON()); | ||
exceptionEventString += exceptionEvent.toString(); | ||
} | ||
break; | ||
} | ||
} | ||
// process not deleted and not existing exception occurrence - create new exception | ||
if (occurrenceEventIdIteration === occurrenceEventId && !exceptionAdded) { | ||
const calDataOccurrence = ICAL.parse(`BEGIN:VCALENDAR\r\n${eventString}\r\nEND:VCALENDAR`); | ||
const compOccurrence = new ICAL.Component(calDataOccurrence); | ||
const veventOccurrence = compOccurrence.getFirstSubcomponent('vevent'); | ||
const icalEvent: ICAL.EventOccurrence = new ICAL.Event(veventOccurrence); | ||
exceptionEventString += '\r\n'; | ||
currentOccurrenceDate = new ICAL.Time(startDate); | ||
exceptionEventString += this.updateEventProperties(eventUrl, icalEvent, title, description, location, startDate, endDate, attendees, categories, organizerEmail, prodid, privateEvent, undefined, undefined, undefined, alarms, new ICAL.Time(next.toJSON())); | ||
} | ||
if (!currentOccurrenceDate) { | ||
currentOccurrenceDate = next; | ||
} | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
if (previousOccurrenceDate && currentOccurrenceDate) { | ||
const currentOccurrenceDateTime = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
if (previousOccurrenceDate === currentOccurrenceDateTime) { | ||
throw new Error('Two occurrences cannot occur on the same day.'); | ||
} | ||
if (previousOccurrenceDate > currentOccurrenceDateTime) { | ||
throw new Error('Cannot reschedule an occurrence of the recurring appointment if it skips over a later occurrence of the same appointment.'); | ||
} | ||
} | ||
} else if (occurrenceEventIdIteration === (occurrenceEventId + 1)) { | ||
if (previousOccurrenceDate && currentOccurrenceDate) { | ||
const currentOccurrenceDateTime = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
if (previousOccurrenceDate === currentOccurrenceDateTime) { | ||
throw new Error('Two occurrences cannot occur on the same day!'); | ||
} | ||
if (previousOccurrenceDate > currentOccurrenceDateTime) { | ||
throw new Error('Cannot reschedule an occurrence of the recurring appointment if it skips over a later occurrence of the same appointment!'); | ||
} | ||
} | ||
} | ||
if (currentOccurrenceDate) { | ||
previousOccurrenceDate = this.getDateWithoutTime(currentOccurrenceDate.toJSDate()).getTime(); | ||
} | ||
} | ||
@@ -311,3 +413,2 @@ } else { | ||
eventString += exceptionEventString; | ||
await this.service.createUpdateEvent(`BEGIN:VCALENDAR\r\nVERSION:2.0\r\nPRODID:${prodid}\r\n` + eventString + '\r\nEND:VCALENDAR', eventUrl); | ||
@@ -319,3 +420,7 @@ } catch(e) { | ||
updateEventProperties = (eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[]): string => { | ||
private getDateWithoutTime(date: Date) { | ||
return new Date(date.toDateString()); | ||
} | ||
updateEventProperties = (eventUrl: string, event: ICAL.Event, title: string, description: string, location: string, startDate: ICAL.TimeJsonData, endDate: ICAL.TimeJsonData, attendees: Attendee[], categories: string[], organizerEmail: string, prodid: string, privateEvent: boolean, frequencyInterval?: EventFrequencyCalendarService, endDateFrequency?: ICAL.TimeJsonData, countFrequency?: number, alarms?: Alarm[], recurrenceId?: Time): string => { | ||
try{ | ||
@@ -341,2 +446,3 @@ logger.debug(`Method updateEventProperties executed for eventUrl: ${eventUrl}`, | ||
const tempId = event.uid; | ||
const originalSubcomponents = event.component.getAllSubcomponents(); | ||
@@ -361,8 +467,9 @@ const originalProperties = event.component.getAllProperties(); | ||
event.component = newEventComponent; | ||
event.uid = tempId; // above line sometimes erasy uid from ocurrence event (when all occurrences are exceptions in recurrence event, then first and last lost uid) | ||
event.component.removeAllProperties('categories'); | ||
event.component.removeAllProperties('attendee'); | ||
event.component.removeAllProperties('class'); | ||
if (privateEvent) { | ||
event.component.removeAllProperties('class'); | ||
event.component.addPropertyWithValue('CLASS', 'PRIVATE'); | ||
@@ -387,4 +494,12 @@ } | ||
this.addFrequency(event, frequencyInterval, endDateFrequency, countFrequency); | ||
} else { | ||
event.component.removeAllProperties('rrule'); | ||
} | ||
if (recurrenceId) { | ||
recurrenceId.convertToZone(ICAL.Timezone.utcTimezone); | ||
event.component.removeAllProperties('recurrence-id'); | ||
event.component.addPropertyWithValue('RECURRENCE-ID', recurrenceId.toICALString()); | ||
} | ||
event.summary = title; | ||
@@ -396,2 +511,3 @@ event.description = description; | ||
if (alarms?.length) { | ||
@@ -427,21 +543,75 @@ for (const alarm of alarms) { | ||
deleteOccurrenceEvent = async(eventUrl: string, event: ICAL.Event, exceptionEventId: number, prodid: string): Promise<void> => { | ||
deleteOccurrenceEvent = async(eventUrl: string, event: ICAL.Event, occurrenceEventId: number, prodid: string): Promise<void> => { | ||
let eventString; | ||
try { | ||
let exceptions: any = {}; | ||
if (event.exceptions) { | ||
exceptions = event.exceptions; | ||
} | ||
let exceptionEventString = ''; | ||
let exceptionEventIdIteration = 1; | ||
for (const exception in exceptions) { | ||
if (exceptionEventIdIteration === exceptionEventId) { | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(exception.toString()), true)); | ||
} else { | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += '\n'; | ||
exceptionEventString += exceptionEvent.toString(); | ||
let newExceptionAdded = false; | ||
if (occurrenceEventId) { | ||
const iterator = event.iterator(); | ||
let occurrenceEventIdIteration = 0; | ||
const exDates = event.component.getAllProperties('exdate'); | ||
const exDatesValues: number[] = []; | ||
for (const exDate of exDates) { | ||
exDatesValues.push(new Date(exDate.getFirstValue().toString()).getTime()); | ||
} | ||
exceptionEventIdIteration += 1; | ||
let exceptions: any = {}; | ||
if (event.exceptions) { | ||
exceptions = event.exceptions; | ||
} | ||
let exceptionProcessed = 0; | ||
for (let next = iterator.next(); next; next = iterator.next()) { | ||
if (exceptionProcessed === exceptions.length && occurrenceEventIdIteration > occurrenceEventId) { | ||
break; | ||
} | ||
occurrenceEventIdIteration += 1; | ||
if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
throw new Error('CalDavClient.UpdateEvent: Occurrence has been already deleted!'); | ||
} | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
continue; | ||
} | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
exceptionProcessed += 1; | ||
exceptionEventString += '\r\n'; | ||
if (occurrenceEventIdIteration === occurrenceEventId) { | ||
newExceptionAdded = true; | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(exception.toString()), true)); | ||
} else { | ||
const exceptionEvent: ICAL.Event = new ICAL.Event(exceptions[exception.toString()].component); | ||
exceptionEventString += exceptionEvent.toString(); | ||
} | ||
break; | ||
} | ||
} | ||
// process not deleted and not existing exception occurrence - create new exception | ||
if (occurrenceEventIdIteration === occurrenceEventId && !exceptionAdded) { | ||
newExceptionAdded = true; | ||
event.component.addPropertyWithValue('EXDATE', ICAL.Time.fromJSDate(new Date(next.toString()), true)); | ||
} | ||
} | ||
if (occurrenceEventIdIteration === 1) { | ||
await this.deleteEvent(eventUrl); | ||
return; | ||
} | ||
} | ||
let eventString = event.toString(); | ||
if (!newExceptionAdded) { | ||
throw new Error(`CalDavClient.UpdateEvent: There is no occurrenceEventId: ${occurrenceEventId}`); | ||
} | ||
eventString = event.toString(); | ||
eventString += exceptionEventString; | ||
@@ -473,3 +643,4 @@ | ||
private parseListOfEvents = async(responseData: string, method: string, startDateFilter?: Date, endDateFilter?: Date): Promise<ICAL.EventWithExceptions[]> => { | ||
private parseListOfEvents = async(responseData: string, method: string, startDateFilter?: Date, endDateFilter?: Date): Promise<ICAL.EventOccurrence[]> => { | ||
if (!endDateFilter) { | ||
@@ -479,3 +650,3 @@ throw new Error('End date of filter is mandatory to avoid parsing infinite recurrence events!'); | ||
const eventsData = await this.parser.parseListOfEvents(responseData); | ||
const events: ICAL.EventWithExceptions[] = []; | ||
const events: ICAL.EventOccurrence[] = []; | ||
if(eventsData.length){ | ||
@@ -487,3 +658,3 @@ for(const eventData of eventsData){ | ||
if (vevent) { | ||
const iCalEvent: ICAL.EventWithExceptions = new ICAL.Event(vevent); | ||
const iCalEvent: ICAL.EventOccurrence = new ICAL.Event(vevent); | ||
const urlParts = eventData.url.split('/'); | ||
@@ -495,3 +666,3 @@ iCalEvent.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
const exDates = iCalEvent.component.getAllProperties('exdate'); | ||
const exDatesValues: any[] = []; | ||
const exDatesValues: number[] = []; | ||
for (const exDate of exDates) { | ||
@@ -505,3 +676,3 @@ exDatesValues.push(new Date(exDate.getFirstValue().toString()).getTime()); | ||
} | ||
let exceptionEventId = 1; | ||
let occurrenceEventId = 0; | ||
const iterator = iCalEvent.iterator(); | ||
@@ -515,2 +686,3 @@ | ||
} | ||
occurrenceEventId += 1; | ||
@@ -522,22 +694,27 @@ if (exDatesValues.find(x => x === new Date(next.toString()).getTime())) { | ||
let isException = false; | ||
let exceptionAdded = false; | ||
for (const exception in exceptions) { | ||
if (new Date(exception.toString()).getTime() === new Date(next.toString()).getTime()) { | ||
isException = true; | ||
const event: ICAL.EventWithExceptions = new ICAL.Event(exceptions[exception.toString()].component); | ||
let exceptionString = exception.toString(); | ||
if (!exceptionString.includes('T') && next.toString().includes('T')) { | ||
// exception is at midnight, we need to add time | ||
exceptionString += 'T00:00:00'; | ||
} | ||
if (new Date(exceptionString).getTime() === new Date(next.toString()).getTime()) { | ||
exceptionAdded = true; | ||
const event: ICAL.EventOccurrence = new ICAL.Event(exceptions[exception.toString()].component); | ||
if (this.eventIncludedInDates(event.startDate, event.endDate, startDateFilter, endDateFilter)) { | ||
event.exceptionEventId = exceptionEventId; | ||
exceptionEventId += 1; | ||
event.occurrenceEventId = occurrenceEventId; | ||
this.extendEventWithRecurrenceDetails(event); | ||
events.push(event); | ||
} | ||
break; | ||
} | ||
} | ||
if (!isException) { | ||
if (!exceptionAdded) { | ||
// process not deleted and not exception occurrence | ||
const compOccurrence = new ICAL.Component(calDataOccurrence); | ||
const veventOccurrence = compOccurrence.getFirstSubcomponent('vevent'); | ||
const icalEvent = new ICAL.Event(veventOccurrence); | ||
const icalEvent: ICAL.EventOccurrence = new ICAL.Event(veventOccurrence); | ||
const diffInMs = new Date(next.toString()).getTime() - new Date(iCalEvent.startDate.toString()).getTime(); | ||
@@ -548,2 +725,3 @@ const diffInDays = diffInMs / (1000 * 60 * 60 * 24); | ||
if (this.eventIncludedInDates(icalEvent.startDate, icalEvent.endDate, startDateFilter, endDateFilter)) { | ||
icalEvent.occurrenceEventId = occurrenceEventId; | ||
icalEvent.component.addPropertyWithValue('url', urlParts[urlParts.length - 1]); | ||
@@ -558,3 +736,2 @@ this.extendEventWithRecurrenceDetails(icalEvent); | ||
} | ||
logger.debug(`${method}: ${events.length} events successfully parsed.`); | ||
@@ -564,3 +741,3 @@ return events; | ||
private extendEventWithRecurrenceDetails(iCalEvent: EventWithExceptions): ICAL.EventWithExceptions { | ||
private extendEventWithRecurrenceDetails(iCalEvent: EventOccurrence): ICAL.EventOccurrence { | ||
const rrule = iCalEvent.component.getFirstPropertyValue('rrule'); | ||
@@ -659,3 +836,3 @@ if (rrule) { | ||
private addFrequency(event: ICAL.Event, freq?: EventFrequencyCalendarService, endDate?: TimeJsonData, count?: number): ICAL.EventWithExceptions { | ||
private addFrequency(event: ICAL.Event, freq?: EventFrequencyCalendarService, endDate?: TimeJsonData, count?: number): ICAL.EventOccurrence { | ||
if (endDate && count) { | ||
@@ -662,0 +839,0 @@ throw new Error('CalDavClient.addFrequency: It is allowed to use endDate or count, not both at the same time.'); |
@@ -36,4 +36,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ | ||
class EventWithExceptions extends Event { | ||
exceptionEventId?: number; | ||
class EventOccurrence extends Event { | ||
occurrenceEventId?: number; | ||
frequency?: FrequencyValues; | ||
@@ -40,0 +40,0 @@ endDateRecurrence?: Time; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
120830
2415