-
Notifications
You must be signed in to change notification settings - Fork 12k
feat(event-types): add team members as optional guests #22651
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -70,14 +70,29 @@ export default class ExchangeCalendarService implements Calendar { | |||||||||||||||||||
| appointment.End = DateTime.Parse(event.endTime); // moment string | ||||||||||||||||||||
| appointment.Location = event.location || "Location not defined!"; | ||||||||||||||||||||
| appointment.Body = new MessageBody(event.description || ""); // you can not use any special character or escape the content | ||||||||||||||||||||
| // Create a set of optional guest emails for easy lookup. | ||||||||||||||||||||
| const optionalGuestEmails = new Set( | ||||||||||||||||||||
| event.optionalGuestTeamMembers?.map((guest) => guest.email.toLowerCase()) ?? [] | ||||||||||||||||||||
| ); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add the main booker as required | ||||||||||||||||||||
| for (let i = 0; i < event.attendees.length; i++) { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(event.attendees[i].email)); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add team members as required, ONLY if they aren't optional | ||||||||||||||||||||
| if (event.team?.members) { | ||||||||||||||||||||
| event.team.members.forEach((member) => { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| event.team.members | ||||||||||||||||||||
| .filter((member) => member.email && !optionalGuestEmails.has(member.email.toLowerCase())) | ||||||||||||||||||||
| .forEach((member) => { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add optional members to the optional list | ||||||||||||||||||||
| if (event.optionalGuestTeamMembers) { | ||||||||||||||||||||
| event.optionalGuestTeamMembers.forEach((member) => { | ||||||||||||||||||||
| appointment.OptionalAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
|
@@ -87,7 +102,7 @@ export default class ExchangeCalendarService implements Calendar { | |||||||||||||||||||
| uid: appointment.Id.UniqueId, | ||||||||||||||||||||
| id: appointment.Id.UniqueId, | ||||||||||||||||||||
| password: "", | ||||||||||||||||||||
| type: "", | ||||||||||||||||||||
| type: "exchange2016_calendar", // Added type for clarity | ||||||||||||||||||||
| url: "", | ||||||||||||||||||||
| additionalInfo: {}, | ||||||||||||||||||||
| }; | ||||||||||||||||||||
|
|
@@ -110,12 +125,33 @@ export default class ExchangeCalendarService implements Calendar { | |||||||||||||||||||
| appointment.End = DateTime.Parse(event.endTime); // moment string | ||||||||||||||||||||
| appointment.Location = event.location || "Location not defined!"; | ||||||||||||||||||||
| appointment.Body = new MessageBody(event.description || ""); // you can not use any special character or escape the content | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Clear old attendees before adding new ones | ||||||||||||||||||||
| appointment.RequiredAttendees.Clear(); | ||||||||||||||||||||
| appointment.OptionalAttendees.Clear(); | ||||||||||||||||||||
|
|
||||||||||||||||||||
| const optionalGuestEmails = new Set( | ||||||||||||||||||||
| event.optionalGuestTeamMembers?.map((guest) => guest.email.toLowerCase()) ?? [] | ||||||||||||||||||||
| ); | ||||||||||||||||||||
|
|
||||||||||||||||||||
|
Comment on lines
+133
to
+136
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Mirror the createEvent hardening: filter undefined emails and add Set typing. Same risk here for Apply this diff: - const optionalGuestEmails = new Set(
- event.optionalGuestTeamMembers?.map((guest) => guest.email.toLowerCase()) ?? []
- );
+ const optionalGuestEmails = new Set<string>(
+ (event.optionalGuestTeamMembers ?? [])
+ .map((guest) => guest?.email?.trim())
+ .filter((e): e is string => !!e)
+ .map((e) => e.toLowerCase())
+ );📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| // Add the main booker as required | ||||||||||||||||||||
| for (let i = 0; i < event.attendees.length; i++) { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(event.attendees[i].email)); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add team members as required, ONLY if they aren't optional | ||||||||||||||||||||
| if (event.team?.members) { | ||||||||||||||||||||
| event.team.members.forEach((member) => { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| event.team.members | ||||||||||||||||||||
| .filter((member) => member.email && !optionalGuestEmails.has(member.email.toLowerCase())) | ||||||||||||||||||||
| .forEach((member) => { | ||||||||||||||||||||
| appointment.RequiredAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
|
|
||||||||||||||||||||
| // Add optional members to the optional list | ||||||||||||||||||||
| if (event.optionalGuestTeamMembers) { | ||||||||||||||||||||
| event.optionalGuestTeamMembers.forEach((member) => { | ||||||||||||||||||||
| appointment.OptionalAttendees.Add(new Attendee(member.email)); | ||||||||||||||||||||
| }); | ||||||||||||||||||||
| } | ||||||||||||||||||||
| appointment.Update( | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -401,27 +401,45 @@ export default class FeishuCalendarService implements Calendar { | |
|
|
||
| private translateAttendees = (event: CalendarEvent): FeishuEventAttendee[] => { | ||
| const attendeeArray: FeishuEventAttendee[] = []; | ||
| event.attendees | ||
| .filter((att) => att.email) | ||
| .forEach((att) => { | ||
| const attendee: FeishuEventAttendee = { | ||
| // Use a Set to track all emails and prevent any duplicates in the final list | ||
| const addedEmails = new Set<string>(); | ||
|
|
||
| // Helper function to add attendees if they haven't been added yet | ||
| const addUniqueAttendee = (email: string, is_optional: boolean) => { | ||
| if (email && !addedEmails.has(email.toLowerCase())) { | ||
| attendeeArray.push({ | ||
| type: "third_party", | ||
| is_optional: false, | ||
| third_party_email: att.email, | ||
| }; | ||
| attendeeArray.push(attendee); | ||
| }); | ||
| is_optional, | ||
| third_party_email: email, | ||
| }); | ||
| addedEmails.add(email.toLowerCase()); | ||
| } | ||
| }; | ||
|
|
||
| // 1. Create a Set of optional guest emails for easy lookup | ||
| const optionalGuestEmails = new Set( | ||
| event.optionalGuestTeamMembers?.map((guest) => guest.email.toLowerCase()) ?? [] | ||
| ); | ||
|
Comment on lines
+420
to
+422
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guard against undefined emails to avoid runtime errors; also normalize/trim before dedup. If any optionalGuestTeamMembers entry lacks email, calling toLowerCase() will throw. Additionally, trimming avoids duplicate entries that differ only by whitespace. Apply this small hardening: - const optionalGuestEmails = new Set(
- event.optionalGuestTeamMembers?.map((guest) => guest.email.toLowerCase()) ?? []
- );
+ const optionalGuestEmails = new Set(
+ (event.optionalGuestTeamMembers ?? [])
+ .map((guest) => guest.email?.trim())
+ .filter((e): e is string => !!e)
+ .map((e) => e.toLowerCase())
+ );And make addUniqueAttendee robust to stray whitespace and compute lowercased value once: - const addUniqueAttendee = (email: string, is_optional: boolean) => {
- if (email && !addedEmails.has(email.toLowerCase())) {
- attendeeArray.push({
- type: "third_party",
- is_optional,
- third_party_email: email,
- });
- addedEmails.add(email.toLowerCase());
- }
- };
+ const addUniqueAttendee = (email: string, is_optional: boolean) => {
+ const trimmed = email?.trim();
+ const lc = trimmed?.toLowerCase();
+ if (lc && !addedEmails.has(lc)) {
+ attendeeArray.push({
+ type: "third_party",
+ is_optional,
+ third_party_email: trimmed,
+ });
+ addedEmails.add(lc);
+ }
+ };Also applies to: 408-417 🤖 Prompt for AI Agents |
||
|
|
||
| // 2. Add the main booker as a required attendee | ||
| event.attendees.forEach((attendee) => addUniqueAttendee(attendee.email, false)); | ||
|
|
||
| // 3. Add the REQUIRED team members, filtering out optionals and the current user | ||
| event.team?.members.forEach((member) => { | ||
| if (member.email !== this.credential.user?.email) { | ||
| const attendee: FeishuEventAttendee = { | ||
| type: "third_party", | ||
| is_optional: false, | ||
| third_party_email: member.email, | ||
| }; | ||
| attendeeArray.push(attendee); | ||
| if ( | ||
| member.email && | ||
| member.email !== this.credential.user?.email && | ||
| !optionalGuestEmails.has(member.email.toLowerCase()) | ||
| ) { | ||
| addUniqueAttendee(member.email, false); | ||
| } | ||
| }); | ||
|
|
||
| // 4. Add the OPTIONAL team members | ||
| event.optionalGuestTeamMembers?.forEach((guest) => { | ||
| addUniqueAttendee(guest.email, true); | ||
| }); | ||
|
|
||
| return attendeeArray; | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Harden optional guest email normalization (avoid runtime TypeError) and add typing.
If any optionalGuestTeamMembers entries are missing email,
.toLowerCase()will throw. Also, type the Set tostringto keep TS strictness.Apply this diff:
📝 Committable suggestion
🤖 Prompt for AI Agents