Skip to content

Commit

Permalink
Schedule status parameter, DQUOTE parsing [Issue #43] (#48)
Browse files Browse the repository at this point in the history
* Add #43 schedule status parameter, dquote parsing

Closes #43

* Fix #43 invalid parameter type

* Fix #43 incorrect variable name
  • Loading branch information
iammatis committed Sep 6, 2020
1 parent c411c18 commit 7ad33fa
Show file tree
Hide file tree
Showing 13 changed files with 66 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added citation for regex from Ruby iCalendar library
- Added rfc link to readme
- Updated dtStamp as optional, current UTC time used when not set
- Added schedule status event parameter, dquote parsing

## [0.4.0] - 2020-05-07

Expand Down
10 changes: 6 additions & 4 deletions src/formatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@ class Formatter implements IFormatter {

public formatOrganizer(organizer?: Organizer): string {
if (organizer) {
const { address, cn, dir, sentBy } = organizer
const { address, cn, dir, sentBy, scheduleStatus } = organizer
const optionals = [
cn ? `CN=${cn}` : '',
dir ? `DIR="${dir}"` : '',
sentBy ? `SENT-BY="mailto:${sentBy}"` : ''
sentBy ? `SENT-BY="mailto:${sentBy}"` : '',
scheduleStatus ? `SCHEDULE-STATUS="${(Array.isArray(scheduleStatus) ? scheduleStatus : [ scheduleStatus ]).join(',')}"` : ''
].filter(Boolean).join(';')

const line = optionals
Expand Down Expand Up @@ -126,7 +127,7 @@ class Formatter implements IFormatter {

public formatAttendee(attendee?: Attendee): string {
if (attendee) {
const { address, cn, dir, sentBy, cu, member, role, partstat, rsvp, delegatedTo, delegatedFrom } = attendee
const { address, cn, dir, sentBy, cu, member, role, partstat, rsvp, delegatedTo, delegatedFrom, scheduleStatus } = attendee
const optionals = [
cn ? `CN=${cn}` : '',
dir ? `DIR=${dir}` : '',
Expand All @@ -137,7 +138,8 @@ class Formatter implements IFormatter {
partstat ? `PARTSTAT=${partstat}` : '',
rsvp ? `RSVP=${rsvp}` : '',
delegatedTo ? `DELEGATED-TO=${delegatedTo.map(del => `"mailto:${del}"`).join(',')}` : '',
delegatedFrom ? `DELEGATED-FROM=${delegatedFrom.map(del => `"mailto:${del}"`).join(',')}` : ''
delegatedFrom ? `DELEGATED-FROM=${delegatedFrom.map(del => `"mailto:${del}"`).join(',')}` : '',
scheduleStatus ? `SCHEDULE-STATUS="${(Array.isArray(scheduleStatus) ? scheduleStatus : [ scheduleStatus ]).join(',')}"` : ''
].filter(Boolean).join(';')

const line = optionals
Expand Down
1 change: 1 addition & 0 deletions src/parser/properties/attendee.parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const validParameters: Parameters = {
'RSVP': 'rsvp',
'DELEGATED-TO': 'delegatedTo',
'DELEGATED-FROM': 'delegatedFrom',
'SCHEDULE-STATUS': { name: 'scheduleStatus', lambda: (text: string): string | string[] => text.includes(',') ? text.split(',') : text }
}

class AttendeeParser extends BaseParser<Attendee> {
Expand Down
14 changes: 12 additions & 2 deletions src/parser/properties/base.parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import IPropertyParser, { Parameters } from '../../types/classes/parsers/property.parser'

const DQUOTES = /^"(?<value>.*)"$/

class BaseParser<Property> implements IPropertyParser<Property> {
public parse(value: string, params = ''): Property {
throw new Error('Method not implemented.')
Expand All @@ -17,9 +19,17 @@ class BaseParser<Property> implements IPropertyParser<Property> {
const [ key, value ] = param.split(/=(.+)?/)

if (key in validParams) {
paramsParsed[validParams[key]] = value
const { groups = {} } = value.match(DQUOTES) || {};

const parameter = validParams[key];
if (typeof parameter === 'object') {
paramsParsed[parameter.name] = parameter.lambda(groups.value || value)
} else {
paramsParsed[parameter] = groups.value || value
}

} else {
// Don't throw an error, just dicard the parameter
// Don't throw an error, just discard the parameter
console.log(`Discarding not recognized parameter '${key}'.`)
}
}
Expand Down
11 changes: 6 additions & 5 deletions src/parser/properties/duration.parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,19 @@ class DurationParser extends BaseParser<Duration> {
throw new ParsingError(`Invalid iCalendar duration '${duration}'`)
}

const [ , value, dur ] = matchArr
if (!(dur in parameters)) {
throw new ParsingError(`Invalid iCalendar duration\'s parameter '${dur}'`)
const [ , value, attribute ] = matchArr
if (!(attribute in parameters)) {
throw new ParsingError(`Invalid iCalendar duration\'s parameter '${attribute}'`)
}

const type = parameters[dur]
const parameter = parameters[attribute]
const amount = Number(value)
if (isNaN(amount)) {
throw new ParsingError(`Invalid iCalendar duration '${duration}'`)
}

result[type] = amount
const key = typeof parameter === 'object' ? parameter.name : parameter
result[key] = amount
}

return result
Expand Down
3 changes: 2 additions & 1 deletion src/parser/properties/organizer.parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import BaseParser from './base.parser'
const validParameters: Parameters = {
'CN': 'cn',
'DIR': 'dir',
'SENT-BY': 'sentBy'
'SENT-BY': 'sentBy',
'SCHEDULE-STATUS': { name: 'scheduleStatus', lambda: (text: string): string | string[] => text.includes(',') ? text.split(',') : text }
}

class OrganizerParser extends BaseParser<Organizer> {
Expand Down
2 changes: 1 addition & 1 deletion src/types/classes/parsers/property.parser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export type Parameters<T = string> = {
[key: string]: T
[key: string]: T | {name: T, lambda: Function}
}

interface IPropertyParser<Property> {
Expand Down
1 change: 1 addition & 0 deletions src/types/general.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export type Person = {
cn?: string
dir?: string
sentBy?: string
scheduleStatus?: string | string[]
}

export type CUType = 'INDIVIDUAL' | 'GROUP' | 'RESOURCE' | 'ROOM' | 'UNKNOWN'
Expand Down
6 changes: 4 additions & 2 deletions tests/builder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ describe('Test Builder Class', () => {
{
cn: 'Henry Cabot',
address: 'hcabot@example.com',
role: 'REQ-PARTICIPANT'
role: 'REQ-PARTICIPANT',
scheduleStatus: '2.0'
},
{
address: 'ildoit@example.com',
role: 'REQ-PARTICIPANT',
delegatedFrom: [ 'bob@example.com' ],
partstat: 'ACCEPTED',
cn: 'Jane Doe'
cn: 'Jane Doe',
scheduleStatus: [ '2.0', '2.4' ]
}
],
xProps: [
Expand Down
6 changes: 4 additions & 2 deletions tests/fixtures/events/multiple_attendees.ics
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ PRODID:-//xyz Corp//NONSGML PDA Calendar Version 1.0//EN
BEGIN:VEVENT
DTSTAMP:20101231T083000Z
UID:uid1@example.com
ATTENDEE;CN=Henry Cabot;ROLE=REQ-PARTICIPANT:mailto:hcabot@example.com
ATTENDEE;CN=Henry Cabot;ROLE=REQ-PARTICIPANT;SCHEDULE-STATUS="2.0":mailto:h
cabot@example.com
ATTENDEE;CN=Jane Doe;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;DELEGATED-FROM=
"mailto:bob@example.com":mailto:ildoit@example.com
"mailto:bob@example.com";SCHEDULE-STATUS="2.0,2.4":mailto:ildoit@example.co
m
X-TESTNAME:random xprop value
END:VEVENT
END:VCALENDAR
23 changes: 22 additions & 1 deletion tests/parser/attendee.parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ describe('Test Attendee Parser', () => {
address: 'hcabot@example.com'
})
})

it('Test simple attendee with schedule status', () => {
const attendee = parser.parse('mailto:hcabot@example.com', 'CN=Henry Cabot;ROLE=REQ-PARTICIPANT;SCHEDULE-STATUS="2.0"')

expect(attendee).toEqual({
cn: 'Henry Cabot',
role: 'REQ-PARTICIPANT',
address: 'hcabot@example.com',
scheduleStatus: '2.0'
})
})

it('Test simple attendee with array of schedule statuses', () => {
const attendee = parser.parse('mailto:hcabot@example.com', 'CN=Henry Cabot;ROLE=REQ-PARTICIPANT;SCHEDULE-STATUS="2.0,2.4"')
expect(attendee).toEqual({
cn: 'Henry Cabot',
role: 'REQ-PARTICIPANT',
address: 'hcabot@example.com',
scheduleStatus: [ '2.0', '2.4' ]
})
})

it('Test simple attendee with multiple mails', () => {
const attendee = parser.parse('mailto:ildoit@example.com', 'CN=Jane Doe;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;DELEGATED-FROM="mailto:bob@example.com"')
Expand All @@ -48,7 +69,7 @@ describe('Test Attendee Parser', () => {
cn: 'Jane Doe',
role: 'REQ-PARTICIPANT',
partstat: 'ACCEPTED',
delegatedFrom: '"mailto:bob@example.com"',
delegatedFrom: 'mailto:bob@example.com',
address: 'ildoit@example.com'
})
})
Expand Down
2 changes: 1 addition & 1 deletion tests/parser/organizer.parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Test Organizer Parser', () => {

expect(organizer).toEqual({
cn: 'Jane Doe',
dir: '"ldap://example.com:6666/o=DC%20Associates,c=US???(cn=John%20Smith)"',
dir: 'ldap://example.com:6666/o=DC%20Associates,c=US???(cn=John%20Smith)',
address: 'ildoit@example.com'
})
})
Expand Down
8 changes: 5 additions & 3 deletions tests/parser/parser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,16 @@ describe('Test Parser', () => {
{
cn: 'Henry Cabot',
role: 'REQ-PARTICIPANT',
address: 'hcabot@example.com'
address: 'hcabot@example.com',
scheduleStatus: '2.0'
},
{
cn: 'Jane Doe',
role: 'REQ-PARTICIPANT',
partstat: 'ACCEPTED',
delegatedFrom: '"mailto:bob@example.com"',
address: 'ildoit@example.com'
delegatedFrom: 'mailto:bob@example.com',
address: 'ildoit@example.com',
scheduleStatus: [ '2.0', '2.4' ]
}
],
xProps: [
Expand Down

0 comments on commit 7ad33fa

Please sign in to comment.