Skip to content

Commit

Permalink
handle RecipientsNotFoundError for calendar event cancellations, #2975
Browse files Browse the repository at this point in the history
  • Loading branch information
vaf-hub committed Apr 27, 2021
1 parent 2a2f088 commit fe34a0a
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 2 deletions.
16 changes: 16 additions & 0 deletions src/calendar/CalendarUpdateDistributor.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {CalendarEventAttendee} from "../api/entities/tutanota/CalendarEvent
import {createCalendarEventAttendee} from "../api/entities/tutanota/CalendarEventAttendee"
import {isTutanotaMailAddress} from "../api/common/RecipientInfo"
import {createMailAddress} from "../api/entities/tutanota/MailAddress"
import {RecipientsNotFoundError} from "../api/common/error/RecipientsNotFoundError"

export interface CalendarUpdateDistributor {
sendInvite(existingEvent: CalendarEvent, sendMailModel: SendMailModel): Promise<void>;
Expand Down Expand Up @@ -72,6 +73,21 @@ export class CalendarMailDistributor implements CalendarUpdateDistributor {
body: makeInviteEmailBody(sender, event, message),
event,
sender
}).catch(RecipientsNotFoundError, e => {
// we want to delete the event even if the recipient is not an existing tutanota address
// and just exclude them from sending out updates but leave the event untouched for other recipients
const invalidRecipients = e.message.split("\n")
let hasRemovedRecipient = false
invalidRecipients.forEach(invalidRecipient => {
const recipientInfo = sendMailModel.bccRecipients().find(r => r.mailAddress === invalidRecipient)
if (recipientInfo) {
hasRemovedRecipient = sendMailModel.removeRecipient(recipientInfo, "bcc", false) || hasRemovedRecipient
}
})
// only try sending again if we successfully removed a recipient and there are still other recipients
if (hasRemovedRecipient && sendMailModel.allRecipients().length) {
return this.sendCancellation(event, sendMailModel)
}
})
}

Expand Down
9 changes: 7 additions & 2 deletions src/mail/editor/SendMailModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ApprovalStatus,
ConversationType,
MailFolderType,
MailMethod,
MAX_ATTACHMENT_SIZE,
OperationType,
ReplyType
Expand Down Expand Up @@ -751,8 +752,12 @@ export class SendMailModel {
// catch all of the badness
.catch(RecipientNotResolvedError, () => {throw new UserError("tooManyAttempts_msg")})
.catch(RecipientsNotFoundError, (e) => {
let invalidRecipients = e.message
throw new UserError(() => lang.get("invalidRecipients_msg") + "\n" + invalidRecipients)
if (mailMethod === MailMethod.ICAL_CANCEL) {
//in case of calendar event cancellation we willremove invalid recipients and then delete the event without sending updates
throw e
} else {
throw new UserError(() => lang.get("invalidRecipients_msg") + "\n" + e.message)
}
})
.catch(TooManyRequestsError, () => {throw new UserError(tooManyRequestsError)})
.catch(AccessBlockedError, e => {
Expand Down
34 changes: 34 additions & 0 deletions test/client/calendar/CalendarEventViewModelTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,40 @@ o.spec("CalendarEventViewModel", function () {
o(cancelModel.bccRecipients().map(r => r.mailAddress)).deepEquals([attendee.address.address])
})

o("own event with non-existing internal attendees in own calendar", async function () {
const calendars = makeCalendars("own")
const distributor = makeDistributor()
const nonExistingAttendee = makeAttendee()
const existingAttendee = makeAttendee("other.address@tutanota.com")
const ownAttendee = makeAttendee(encMailAddress.address)
const calendarModel = makeCalendarModel()
const mailModel = makeMailModel(["other.address@tutanota.com"])
const existingEvent = createCalendarEvent({
_id: ["listid", "calendarid"],
_ownerGroup: calendarGroupId,
organizer: encMailAddress,
attendees: [ownAttendee, existingAttendee, nonExistingAttendee],
sequence: "1",
})
const newEvent = createCalendarEvent({
_id: ["listid", "calendarid"],
_ownerGroup: calendarGroupId,
organizer: encMailAddress,
attendees: [ownAttendee, existingAttendee, nonExistingAttendee],
sequence: "2",
startTime: existingEvent.startTime,
endTime: existingEvent.endTime,
})
const viewModel = init({calendars, existingEvent, calendarModel, distributor, mailModel})

await viewModel.deleteEvent()

// This doesn't always pass because sometimes the start and end times are off by a fraction of a second
o(calendarModel.deleteEvent.calls.map(c => c.args)).deepEquals([[existingEvent]])
o(distributor.sendCancellation.calls.map(c => c.args[0])).deepEquals([newEvent])
o(cancelModel.bccRecipients().length).equals(1)
})

o("own event with external attendees in own calendar, has password, not confidential", async function () {
const calendars = makeCalendars("own")
const distributor = makeDistributor()
Expand Down

0 comments on commit fe34a0a

Please sign in to comment.