Skip to content

Commit

Permalink
fix(ics): corrects multi-event order #194, metadata #201
Browse files Browse the repository at this point in the history
This corrects the order of events added using addEvent() such that the
sequence is respected. This also addresses metadata issues for DTSTAMP,
PRODID and UID.
  • Loading branch information
jshor committed Nov 25, 2022
1 parent a0974a0 commit 08f971f
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 36 deletions.
45 changes: 27 additions & 18 deletions src/ICalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export default class ICalendar extends CalendarBase {
this
.setMeta('UID', ics.getUid())
.setMeta('DTSTAMP', time.getTimeCreated())
.setMeta('PRODID', ics.getProdId())
.addProperty('CLASS', 'PUBLIC')
.addProperty('DESCRIPTION', ics.formatText(this.description))
.addProperty('LOCATION', ics.formatText(this.location))
Expand Down Expand Up @@ -113,6 +112,19 @@ export default class ICalendar extends CalendarBase {
return features.join('')
}

/**
* Returns the iCalendar meta properties, formatted as VEVENT entry lines.
*
* @returns {string[]}
*/
public getMeta = (): string[] => {
return Object
.keys(this.meta)
.map((key: string) => {
return `${key}:${this.meta[key]}`
})
}

/**
* Sets iCalendar meta properties, such as UID, DTSTAMP, etc.
*
Expand Down Expand Up @@ -208,28 +220,25 @@ export default class ICalendar extends CalendarBase {
* @returns {string}
*/
public render = (): string => {
const vEvents: string[] = this
.additionalEvents
.concat(this)
.reduce((properties: string[], calendar: ICalendar) => [
...properties,
'BEGIN:VEVENT',
...calendar.properties,
'END:VEVENT'
], [])

const meta: string[] = Object
.keys(this.meta)
.map((key: string) => {
return `${key}:${this.meta[key]}`
})
const events = [
this,
...this.additionalEvents
]

const vEvents = events.reduce((properties: string[], calendar: ICalendar) => [
...properties,
'BEGIN:VEVENT',
...calendar.properties,
...calendar.getMeta(),
'END:VEVENT'
], [])

return [
'BEGIN:VCALENDAR',
'VERSION:2.0',
...vEvents,
'END:VCALENDAR',
...meta
`PRODID:${ics.getProdId()}`,
'END:VCALENDAR'
].join('\n')
}
}
61 changes: 45 additions & 16 deletions src/__tests__/ICalendar.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ describe('ICalendar', () => {
].join('\n'))
})

describe('addEvent()', () => {
const renderIcs = (i: number) => new ICalendar({
title: `Event ${i}`,
location: 'online',
description: 'Example description',
start: new Date('2021-09-01T17:00:00'),
end: new Date('2021-09-01T19:00:00')
})

it('should render with two additional events in the correct order', () => {
const ics = renderIcs(1)

ics.addEvent(renderIcs(2))
ics.addEvent(renderIcs(3))

const titles = ics
.render()
.match(/SUMMARY:Event [0-9]/ig)

expect(titles).toEqual([
'SUMMARY:Event 1',
'SUMMARY:Event 2',
'SUMMARY:Event 3'
])
})
})

describe('addAlarm()', () => {
let obj: ICalendar

Expand Down Expand Up @@ -261,11 +288,11 @@ describe('ICalendar', () => {
'TRANSP:TRANSPARENT',
`DTSTART:${time.formatDate(baseOpts.start, FORMAT.FULL)}`,
`DTEND:${time.formatDate(baseOpts.end, FORMAT.FULL)}`,
'END:VEVENT',
'END:VCALENDAR',
`UID:${mockUuid}`,
`DTSTAMP:${time.getTimeCreated()}`,
'PRODID:foobar'
'END:VEVENT',
'PRODID:foobar',
'END:VCALENDAR'
].join('\n')

expect(rendered).toBe(expected)
Expand All @@ -291,17 +318,6 @@ describe('ICalendar', () => {
'BEGIN:VCALENDAR',
'VERSION:2.0',

// second event
'BEGIN:VEVENT',
'CLASS:PUBLIC',
`DESCRIPTION:${secondEventOpts.description}`,
`LOCATION:${secondEventOpts.location}`,
`SUMMARY:${secondEventOpts.title}`,
'TRANSP:TRANSPARENT',
`DTSTART:${time.formatDate(secondEventOpts.start, FORMAT.FULL)}`,
`DTEND:${time.formatDate(baseOpts.end, FORMAT.FULL)}`,
'END:VEVENT',

// base event
'BEGIN:VEVENT',
'CLASS:PUBLIC',
Expand All @@ -311,12 +327,25 @@ describe('ICalendar', () => {
'TRANSP:TRANSPARENT',
`DTSTART:${time.formatDate(baseOpts.start, FORMAT.FULL)}`,
`DTEND:${time.formatDate(baseOpts.end, FORMAT.FULL)}`,
`UID:${mockUuid}`,
`DTSTAMP:${time.getTimeCreated()}`,
'END:VEVENT',

'END:VCALENDAR',
// second event
'BEGIN:VEVENT',
'CLASS:PUBLIC',
`DESCRIPTION:${secondEventOpts.description}`,
`LOCATION:${secondEventOpts.location}`,
`SUMMARY:${secondEventOpts.title}`,
'TRANSP:TRANSPARENT',
`DTSTART:${time.formatDate(secondEventOpts.start, FORMAT.FULL)}`,
`DTEND:${time.formatDate(baseOpts.end, FORMAT.FULL)}`,
`UID:${mockUuid}`,
`DTSTAMP:${time.getTimeCreated()}`,
'PRODID:foobar'
'END:VEVENT',

'PRODID:foobar',
'END:VCALENDAR'
].join('\n')

expect(rendered).toBe(expected)
Expand Down
2 changes: 1 addition & 1 deletion src/utils/__tests__/time.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ describe('time utils', () => {
})

it('should get the current time in date time format', () => {
expect(time.getTimeCreated()).toBe(time.formatDate(new Date(), FORMAT.DATE))
expect(time.getTimeCreated()).toBe(time.formatDate(new Date(), FORMAT.FULL))
})
})

Expand Down
2 changes: 1 addition & 1 deletion src/utils/time.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const formatDateNoUtc = (d: Date = new Date(), format: string): string => {
* @returns {string}
*/
const getTimeCreated = (): string => {
return formatDate(new Date(), FORMAT.DATE)
return formatDate(new Date(), FORMAT.FULL)
}

/**
Expand Down

0 comments on commit 08f971f

Please sign in to comment.