Skip to content

Commit 2a304a1

Browse files
authored
Merge pull request #18 from linuxfoundation/andrest50/v1-meeting-rsvp
[LFXV2-884] Add meeting invite response event handling for v2 indexing
2 parents e4fc57e + 30361f4 commit 2a304a1

File tree

7 files changed

+431
-212
lines changed

7 files changed

+431
-212
lines changed

charts/lfx-v1-sync-helper/Chart.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ apiVersion: v2
55
name: lfx-v1-sync-helper
66
description: LFX Platform v1 Sync Helper chart
77
type: application
8-
version: 0.3.6
8+
version: 0.3.7

cmd/lfx-v1-sync-helper/handlers.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ func handleKVPut(ctx context.Context, entry jetstream.KeyValueEntry) {
8383
handleZoomMeetingRegistrantUpdate(ctx, key, v1Data)
8484
case "itx-zoom-meetings-mappings-v2":
8585
handleZoomMeetingMappingUpdate(ctx, key, v1Data)
86+
case "itx-zoom-meetings-invite-responses-v2":
87+
handleZoomMeetingInviteResponseUpdate(ctx, key, v1Data)
8688
case "itx-zoom-past-meetings-attendees":
8789
handleZoomPastMeetingAttendeeUpdate(ctx, key, v1Data)
8890
case "itx-zoom-past-meetings-invitees":

cmd/lfx-v1-sync-helper/handlers_meetings.go

Lines changed: 329 additions & 194 deletions
Large diffs are not rendered by default.

cmd/lfx-v1-sync-helper/helper_meetings.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
3434

3535
meetingStartTime, err := time.Parse(time.RFC3339, meeting.StartTime)
3636
if err != nil {
37-
logger.With(errKey, err, "meeting_id", meeting.ID, "start_time", meeting.StartTime).ErrorContext(ctx, "failed to parse meeting start_time")
38-
return nil, err
37+
return nil, fmt.Errorf("failed to parse meeting start_time %s: %w", meeting.StartTime, err)
3938
}
4039

4140
location := time.UTC
@@ -71,8 +70,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
7170
// Convert occurrence start time to timeRFC3339 format to make the time easier to read in the logs
7271
occurrenceStartTimeUnixInt, err := strconv.ParseInt(occurrenceStartTimeUnix, 10, 64)
7372
if err != nil {
74-
logger.With(errKey, err, "meeting_id", meeting.ID, "occurrence_id", occurrenceStartTimeUnix, "occurrence_start_time", occurrenceStartTimeFmt).ErrorContext(ctx, "failed to parse occurrence start_time")
75-
return nil, err
73+
return nil, fmt.Errorf("failed to convert occurrence start time %s to int: %w", occurrenceStartTimeUnix, err)
7674
}
7775
occurrenceStartTimeFmt = time.Unix(occurrenceStartTimeUnixInt, 0).Format(time.RFC3339)
7876
}
@@ -113,8 +111,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
113111
if occurrencePatternIdx < len(occurrencesPattern)-1 && occurrencesPattern[occurrencePatternIdx+1].OccurrenceID != "" {
114112
nextRecurrenceTimeUnix, err = strconv.ParseInt(occurrencesPattern[occurrencePatternIdx+1].OccurrenceID, 10, 64)
115113
if err != nil {
116-
logger.With(errKey, err, "meeting_id", meeting.ID, "next_recurrence_occurrence_id", occurrencesPattern[occurrencePatternIdx+1].OccurrenceID, "next_recurrence_start_time", occurrencesPattern[occurrencePatternIdx+1].StartTime).ErrorContext(ctx, "failed to parse next recurrence start_time")
117-
return nil, err
114+
return nil, fmt.Errorf("failed to convert next recurrence start time %s to int: %w", occurrencesPattern[occurrencePatternIdx+1].OccurrenceID, err)
118115
}
119116
}
120117
logger.With("meeting_id", meeting.ID, "current_recurrence", occurrencePattern, "next_recurrence_start_time_unix", nextRecurrenceTimeUnix, "next_recurrence_start_time", time.Unix(nextRecurrenceTimeUnix, 0).Format(time.RFC3339)).DebugContext(ctx, "next recurrence start time")
@@ -130,8 +127,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
130127
// Convert unix string start time into time.Time object
131128
unixStartTime, err := strconv.ParseInt(occurrencePattern.OccurrenceID, 10, 64)
132129
if err != nil {
133-
logger.With(errKey, err, "meeting_id", meeting.ID, "recurrence_occurrence_id", occurrencePattern.OccurrenceID, "recurrence_start_time", occurrencePattern.StartTime).ErrorContext(ctx, "failed to parse recurrence start_time")
134-
return nil, err
130+
return nil, fmt.Errorf("failed to convert recurrence start time %s to int: %w", occurrencePattern.OccurrenceID, err)
135131
}
136132
recStartTime := time.Unix(unixStartTime, 0)
137133
recStartTime, err = timeInLocation(recStartTime, meeting.Timezone)
@@ -142,8 +138,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
142138
// Get occurrences based on reccurrence pattern and start time
143139
occurrences, err := getRRuleOccurrences(recStartTime, meeting.Timezone, occurrencePattern.Recurrence, nil)
144140
if err != nil {
145-
logger.With(errKey, err, "meeting_id", meeting.ID, "start_time", recStartTime, "recurrence_rrule", occurrencePattern.Recurrence).ErrorContext(ctx, "failed to get recurrence rule")
146-
return nil, err
141+
return nil, fmt.Errorf("failed to get recurrence rule: %w", err)
147142
}
148143
occurrencesInLog := occurrences
149144
// only show the first 100 occurrences to avoid spamming the logs
@@ -201,8 +196,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
201196
currentAgenda = updatedOcc.Agenda
202197
unixStartTime, err := strconv.ParseInt(updatedOcc.NewOccurrenceID, 10, 64)
203198
if err != nil {
204-
logger.With(errKey, err, "meeting_id", meeting.ID, "occurrence", updatedOcc).ErrorContext(ctx, "failed to parse updated occurrence start_time")
205-
return nil, err
199+
return nil, fmt.Errorf("failed to convert updated occurrence start time %s to int: %w", updatedOcc.NewOccurrenceID, err)
206200
}
207201
currentStartTime = time.Unix(unixStartTime, 0).In(location)
208202
logger.With("meeting_id", meeting.ID, "current_start_time", currentStartTime, "occurrence", updatedOcc).DebugContext(ctx, "current start time changed")
@@ -233,8 +227,7 @@ func calculateOccurrences(ctx context.Context, meeting meetingInput, pastOccurre
233227
// Skip past occurrences if no past occurrences are expected
234228
unixStartTime, err := strconv.ParseInt(updatedOcc.NewOccurrenceID, 10, 64)
235229
if err != nil {
236-
logger.With(errKey, err, "meeting_id", meeting.ID, "occurrence_id", o.Unix(), "updated_occ", updatedOcc).ErrorContext(ctx, "failed to parse updated occurrence start_time")
237-
return nil, err
230+
return nil, fmt.Errorf("failed to convert updated occurrence start time %s to int: %w", updatedOcc.NewOccurrenceID, err)
238231
}
239232

240233
// If updated occurrence does not have a duration, use meeting duration
@@ -441,8 +434,7 @@ func getRRule(reccurrence ZoomMeetingRecurrence, endTime *time.Time) (string, er
441434
reccurrence.EndTimes = "0"
442435
t, err := time.Parse(time.RFC3339, reccurrence.EndDateTime)
443436
if err != nil {
444-
logger.With(errKey, err, "recurrence", reccurrence).Error("error parsing recurrence end_date_time")
445-
return "", err
437+
return "", fmt.Errorf("failed to parse recurrence end_date_time %s: %w", reccurrence.EndDateTime, err)
446438
}
447439
rrule.WriteString(fmt.Sprintf("UNTIL=%s;", t.Format("20060102T150405Z")))
448440
}

cmd/lfx-v1-sync-helper/models_meetings.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,95 @@ type registrantInput struct {
454454
UpdatedBy UpdatedBy `json:"updated_by" dynamodbav:"updated_by"`
455455
}
456456

457+
// RSVPResponseType represents the type of RSVP response
458+
type RSVPResponseType string
459+
460+
const (
461+
// RSVPResponseAccepted indicates the registrant will attend
462+
RSVPResponseAccepted RSVPResponseType = "accepted"
463+
// RSVPResponseMaybe indicates the registrant might attend
464+
RSVPResponseMaybe RSVPResponseType = "maybe"
465+
// RSVPResponseDeclined indicates the registrant will not attend
466+
RSVPResponseDeclined RSVPResponseType = "declined"
467+
)
468+
469+
// RSVPScope represents the scope of an RSVP response
470+
type RSVPScope string
471+
472+
const (
473+
// RSVPScopeSingle indicates the RSVP applies to a single occurrence
474+
RSVPScopeSingle RSVPScope = "single"
475+
// RSVPScopeAll indicates the RSVP applies to all occurrences in the series
476+
RSVPScopeAll RSVPScope = "all"
477+
// RSVPScopeThisAndFollowing indicates the RSVP applies to a specific occurrence and all following ones
478+
RSVPScopeThisAndFollowing RSVPScope = "this_and_following"
479+
)
480+
481+
type inviteResponseInput struct {
482+
// ID is the partition key of the invite response (it is a UUID)
483+
ID string `json:"id" dynamodbav:"id"`
484+
485+
// MeetingAndOccurrenceID is the ID of the combined meeting and occurrence associated with the invite response
486+
MeetingAndOccurrenceID string `json:"meeting_and_occurrence_id" dynamodbav:"meeting_and_occurrence_id"`
487+
488+
// MeetingID is the ID of the meeting that the invite response is associated with.
489+
// It is a Global Secondary Index on the invite response table.
490+
MeetingID string `json:"meeting_id" dynamodbav:"meeting_id"`
491+
492+
// OccurrenceID is the ID of the occurrence that the invite response is associated with.
493+
OccurrenceID string `json:"occurrence_id" dynamodbav:"occurrence_id"`
494+
495+
// RegistrantID is the ID of the registrant that the invite response is associated with.
496+
// It is a Global Secondary Index on the invite response table.
497+
RegistrantID string `json:"registrant_id" dynamodbav:"registrant_id"`
498+
499+
// Email is the email of the registrant that the invite response is associated with.
500+
// It is a Global Secondary Index on the invite response table.
501+
Email string `json:"email" dynamodbav:"email"`
502+
503+
// Name is the name of the registrant that the invite response is associated with.
504+
Name string `json:"name" dynamodbav:"name"`
505+
506+
// UserID is the ID of the user that the invite response is associated with.
507+
UserID string `json:"user_id" dynamodbav:"user_id"`
508+
509+
// Username is the LF username of the registrant that the invite response is associated with.
510+
// This is a v2 only attribute, meaning the username is for an LF user in the v2 system.
511+
Username string `json:"username"`
512+
513+
// Org is the organization of the registrant that the invite response is associated with.
514+
Org string `json:"org" dynamodbav:"org"`
515+
516+
// JobTitle is the job title of the registrant that the invite response is associated with.
517+
JobTitle string `json:"job_title" dynamodbav:"job_title"`
518+
519+
// Response is the response of the registrant that the invite response is associated with.
520+
// It is a Global Secondary Index on the invite response table.
521+
Response RSVPResponseType `json:"response" dynamodbav:"response"`
522+
523+
// Scope is the scope of the response (single/all/this_and_following)
524+
// This is only a v2 attribute.
525+
Scope RSVPScope `json:"scope"`
526+
527+
// ResponseDate is the date of the invite response from the registrant.
528+
ResponseDate string `json:"response_date" dynamodbav:"response_date"`
529+
530+
// SESMessageID is the SES message ID of the invite response.
531+
SESMessageID string `json:"ses_message_id" dynamodbav:"ses_message_id"`
532+
533+
// EmailSubject is the subject of the invite response email.
534+
EmailSubject string `json:"email_subject" dynamodbav:"email_subject"`
535+
536+
// EmailText is the text of the invite response email.
537+
EmailText string `json:"email_text" dynamodbav:"email_text"`
538+
539+
// CreatedAt is the timestamp in RFC3339 format of when the invite response was created.
540+
CreatedAt string `json:"created_at" dynamodbav:"created_at"`
541+
542+
// ModifiedAt is the timestamp in RFC3339 format of when the invite response was last modified.
543+
ModifiedAt string `json:"modified_at" dynamodbav:"modified_at"`
544+
}
545+
457546
// pastMeetingInput represents input data for past meeting records.
458547
type pastMeetingInput struct {
459548
// ID is the partition key of the past meeting table

meltano/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ Common environment variables that may need to be set:
9999
- `TAP_POSTGRES_USER`: PostgreSQL username (default: `lfit`)
100100
- `TAP_POSTGRES_DATABASE`: PostgreSQL database name (default: `sfdc`)
101101

102-
For additional PostgreSQL extractor settings, see the [tap-postgres documentation](https://hub.meltano.com/extractors/tap-postgres).
102+
For additional PostgreSQL extractor settings, see the [tap-postgres documentation](https://hub.meltano.com/extractors/tap-postgres/).
103103

104104
#### NATS Configuration
105105

meltano/meltano.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ plugins:
6969
- itx-zoom-past-meetings-recordings
7070
- itx-zoom-past-meetings-summaries
7171
- itx-zoom-meetings-registrants-v2
72+
- itx-zoom-meetings-invite-responses-v2
7273
metadata:
7374
"*":
7475
# Setting the replication key allows conditional loads for

0 commit comments

Comments
 (0)