move event instance funcs to new file (#3659)

No logic changes, just code movement.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [x] 🧹 Tech Debt/Cleanup

#### Test Plan

- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Keepers 2023-06-30 09:50:04 -06:00 committed by GitHub
parent 04841052dd
commit c218dd865b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 216 additions and 206 deletions

View File

@ -0,0 +1,215 @@
package exchange
import (
"context"
"fmt"
"strings"
"time"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/common/str"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api"
)
type eventInstanceAndAttachmenter interface {
attachmentGetDeletePoster
DeleteItem(
ctx context.Context,
userID, itemID string,
) error
GetItemInstances(
ctx context.Context,
userID, itemID string,
startDate, endDate string,
) ([]models.Eventable, error)
PatchItem(
ctx context.Context,
userID, eventID string,
body models.Eventable,
) (models.Eventable, error)
}
func updateRecurringEvents(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID, containerID, itemID string,
event models.Eventable,
errs *fault.Bus,
) error {
if event.GetRecurrence() == nil {
return nil
}
// Cancellations and exceptions are currently in additional data
// but will get their own fields once the beta API lands and
// should be moved then
cancelledOccurrences := event.GetAdditionalData()["cancelledOccurrences"]
exceptionOccurrences := event.GetAdditionalData()["exceptionOccurrences"]
err := updateCancelledOccurrences(ctx, eiaa, userID, itemID, cancelledOccurrences)
if err != nil {
return clues.Wrap(err, "update cancelled occurrences")
}
err = updateExceptionOccurrences(ctx, eiaa, userID, containerID, itemID, exceptionOccurrences, errs)
if err != nil {
return clues.Wrap(err, "update exception occurrences")
}
return nil
}
// updateExceptionOccurrences take events that have exceptions, uses
// the originalStart date to find the instance and modify it to match
// the backup by updating the instance to match the backed up one
func updateExceptionOccurrences(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID string,
containerID string,
itemID string,
exceptionOccurrences any,
errs *fault.Bus,
) error {
if exceptionOccurrences == nil {
return nil
}
eo, ok := exceptionOccurrences.([]any)
if !ok {
return clues.New("converting exceptionOccurrences to []any").
With("type", fmt.Sprintf("%T", exceptionOccurrences))
}
for _, instance := range eo {
instance, ok := instance.(map[string]any)
if !ok {
return clues.New("converting instance to map[string]any").
With("type", fmt.Sprintf("%T", instance))
}
evt, err := api.EventFromMap(instance)
if err != nil {
return clues.Wrap(err, "parsing exception event")
}
start := ptr.Val(evt.GetOriginalStart())
startStr := dttm.FormatTo(start, dttm.DateOnly)
endStr := dttm.FormatTo(start.Add(24*time.Hour), dttm.DateOnly)
ictx := clues.Add(ctx, "event_instance_id", ptr.Val(evt.GetId()), "event_instance_date", start)
// Get all instances on the day of the instance which should
// just the one we need to modify
instances, err := eiaa.GetItemInstances(ictx, userID, itemID, startStr, endStr)
if err != nil {
return clues.Wrap(err, "getting instances")
}
// Since the min recurrence interval is 1 day and we are
// querying for only a single day worth of instances, we
// should not have more than one instance here.
if len(instances) != 1 {
return clues.New("invalid number of instances for modified").
With("instances_count", len(instances), "search_start", startStr, "search_end", endStr)
}
evt = toEventSimplified(evt)
_, err = eiaa.PatchItem(ictx, userID, ptr.Val(instances[0].GetId()), evt)
if err != nil {
return clues.Wrap(err, "updating event instance")
}
// We are creating event again from map as `toEventSimplified`
// removed the attachments and creating a clone from start of
// the event is non-trivial
evt, err = api.EventFromMap(instance)
if err != nil {
return clues.Wrap(err, "parsing event instance")
}
err = updateAttachments(
ictx,
eiaa,
userID,
containerID,
ptr.Val(instances[0].GetId()),
evt,
errs)
if err != nil {
return clues.Wrap(err, "updating event instance attachments")
}
}
return nil
}
// updateCancelledOccurrences get the cancelled occurrences which is a
// list of strings of the format "<id>.<date>", parses the date out of
// that and uses the to get the event instance at that date to delete.
func updateCancelledOccurrences(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID string,
itemID string,
cancelledOccurrences any,
) error {
if cancelledOccurrences == nil {
return nil
}
co, ok := cancelledOccurrences.([]any)
if !ok {
return clues.New("converting cancelledOccurrences to []any").
With("type", fmt.Sprintf("%T", cancelledOccurrences))
}
// OPTIMIZATION: We can fetch a date range instead of fetching
// instances if we have multiple cancelled events which are nearby
// and reduce the number of API calls that we have to make
for _, instance := range co {
instance, err := str.AnyToString(instance)
if err != nil {
return err
}
splits := strings.Split(instance, ".")
startStr := splits[len(splits)-1]
start, err := dttm.ParseTime(startStr)
if err != nil {
return clues.Wrap(err, "parsing cancelled event date")
}
endStr := dttm.FormatTo(start.Add(24*time.Hour), dttm.DateOnly)
// Get all instances on the day of the instance which should
// just the one we need to modify
instances, err := eiaa.GetItemInstances(ctx, userID, itemID, startStr, endStr)
if err != nil {
return clues.Wrap(err, "getting instances")
}
// Since the min recurrence interval is 1 day and we are
// querying for only a single day worth of instances, we
// should not have more than one instance here.
if len(instances) != 1 {
return clues.New("invalid number of instances for cancelled").
With("instances_count", len(instances), "search_start", startStr, "search_end", endStr)
}
err = eiaa.DeleteItem(ctx, userID, ptr.Val(instances[0].GetId()))
if err != nil {
return clues.Wrap(err, "deleting event instance")
}
}
return nil
}

View File

@ -3,16 +3,11 @@ package exchange
import (
"bytes"
"context"
"fmt"
"strings"
"time"
"github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/dttm"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/common/str"
"github.com/alcionai/corso/src/internal/m365/graph"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/control"
@ -191,140 +186,6 @@ func restoreEvent(
return info, nil
}
func updateRecurringEvents(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID, containerID, itemID string,
event models.Eventable,
errs *fault.Bus,
) error {
if event.GetRecurrence() == nil {
return nil
}
// Cancellations and exceptions are currently in additional data
// but will get their own fields once the beta API lands and
// should be moved then
cancelledOccurrences := event.GetAdditionalData()["cancelledOccurrences"]
exceptionOccurrences := event.GetAdditionalData()["exceptionOccurrences"]
err := updateCancelledOccurrences(ctx, eiaa, userID, itemID, cancelledOccurrences)
if err != nil {
return clues.Wrap(err, "update cancelled occurrences")
}
err = updateExceptionOccurrences(ctx, eiaa, userID, containerID, itemID, exceptionOccurrences, errs)
if err != nil {
return clues.Wrap(err, "update exception occurrences")
}
return nil
}
type eventInstanceAndAttachmenter interface {
attachmentGetDeletePoster
DeleteItem(
ctx context.Context,
userID, itemID string,
) error
GetItemInstances(
ctx context.Context,
userID, itemID string,
startDate, endDate string,
) ([]models.Eventable, error)
PatchItem(
ctx context.Context,
userID, eventID string,
body models.Eventable,
) (models.Eventable, error)
}
// updateExceptionOccurrences take events that have exceptions, uses
// the originalStart date to find the instance and modify it to match
// the backup by updating the instance to match the backed up one
func updateExceptionOccurrences(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID string,
containerID string,
itemID string,
exceptionOccurrences any,
errs *fault.Bus,
) error {
if exceptionOccurrences == nil {
return nil
}
eo, ok := exceptionOccurrences.([]any)
if !ok {
return clues.New("converting exceptionOccurrences to []any").
With("type", fmt.Sprintf("%T", exceptionOccurrences))
}
for _, instance := range eo {
instance, ok := instance.(map[string]any)
if !ok {
return clues.New("converting instance to map[string]any").
With("type", fmt.Sprintf("%T", instance))
}
evt, err := api.EventFromMap(instance)
if err != nil {
return clues.Wrap(err, "parsing exception event")
}
start := ptr.Val(evt.GetOriginalStart())
startStr := dttm.FormatTo(start, dttm.DateOnly)
endStr := dttm.FormatTo(start.Add(24*time.Hour), dttm.DateOnly)
ictx := clues.Add(ctx, "event_instance_id", ptr.Val(evt.GetId()), "event_instance_date", start)
// Get all instances on the day of the instance which should
// just the one we need to modify
instances, err := eiaa.GetItemInstances(ictx, userID, itemID, startStr, endStr)
if err != nil {
return clues.Wrap(err, "getting instances")
}
// Since the min recurrence interval is 1 day and we are
// querying for only a single day worth of instances, we
// should not have more than one instance here.
if len(instances) != 1 {
return clues.New("invalid number of instances for modified").
With("instances_count", len(instances), "search_start", startStr, "search_end", endStr)
}
evt = toEventSimplified(evt)
_, err = eiaa.PatchItem(ictx, userID, ptr.Val(instances[0].GetId()), evt)
if err != nil {
return clues.Wrap(err, "updating event instance")
}
// We are creating event again from map as `toEventSimplified`
// removed the attachments and creating a clone from start of
// the event is non-trivial
evt, err = api.EventFromMap(instance)
if err != nil {
return clues.Wrap(err, "parsing event instance")
}
err = updateAttachments(
ictx,
eiaa,
userID,
containerID,
ptr.Val(instances[0].GetId()),
evt,
errs)
if err != nil {
return clues.Wrap(err, "updating event instance attachments")
}
}
return nil
}
type attachmentGetDeletePoster interface {
attachmentPoster
GetAttachments(
@ -346,9 +207,7 @@ type attachmentGetDeletePoster interface {
// the id changes which makes the ids unusable. In this function, we
// use the name and content bytes to detect the changes. This function
// can be used to update the attachments of any event irrespective of
// whether they are event instances of a series master although for
// newer event, since we probably won't already have any events it
// would be better use Post[Small|Large]Attachment.
// whether they are event instances of a series master.
func updateAttachments(
ctx context.Context,
agdp attachmentGetDeletePoster,
@ -445,70 +304,6 @@ func updateAttachments(
return el.Failure()
}
// updateCancelledOccurrences get the cancelled occurrences which is a
// list of strings of the format "<id>.<date>", parses the date out of
// that and uses the to get the event instance at that date to delete.
func updateCancelledOccurrences(
ctx context.Context,
eiaa eventInstanceAndAttachmenter,
userID string,
itemID string,
cancelledOccurrences any,
) error {
if cancelledOccurrences == nil {
return nil
}
co, ok := cancelledOccurrences.([]any)
if !ok {
return clues.New("converting cancelledOccurrences to []any").
With("type", fmt.Sprintf("%T", cancelledOccurrences))
}
// OPTIMIZATION: We can fetch a date range instead of fetching
// instances if we have multiple cancelled events which are nearby
// and reduce the number of API calls that we have to make
for _, instance := range co {
instance, err := str.AnyToString(instance)
if err != nil {
return err
}
splits := strings.Split(instance, ".")
startStr := splits[len(splits)-1]
start, err := dttm.ParseTime(startStr)
if err != nil {
return clues.Wrap(err, "parsing cancelled event date")
}
endStr := dttm.FormatTo(start.Add(24*time.Hour), dttm.DateOnly)
// Get all instances on the day of the instance which should
// just the one we need to modify
instances, err := eiaa.GetItemInstances(ctx, userID, itemID, startStr, endStr)
if err != nil {
return clues.Wrap(err, "getting instances")
}
// Since the min recurrence interval is 1 day and we are
// querying for only a single day worth of instances, we
// should not have more than one instance here.
if len(instances) != 1 {
return clues.New("invalid number of instances for cancelled").
With("instances_count", len(instances), "search_start", startStr, "search_end", endStr)
}
err = eiaa.DeleteItem(ctx, userID, ptr.Val(instances[0].GetId()))
if err != nil {
return clues.Wrap(err, "deleting event instance")
}
}
return nil
}
func (h eventRestoreHandler) getItemsInContainerByCollisionKey(
ctx context.Context,
userID, containerID string,