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:
parent
04841052dd
commit
c218dd865b
215
src/internal/m365/exchange/events_instance_restore.go
Normal file
215
src/internal/m365/exchange/events_instance_restore.go
Normal 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
|
||||||
|
}
|
||||||
@ -3,16 +3,11 @@ package exchange
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"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/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/common/str"
|
|
||||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
"github.com/alcionai/corso/src/pkg/control"
|
"github.com/alcionai/corso/src/pkg/control"
|
||||||
@ -191,140 +186,6 @@ func restoreEvent(
|
|||||||
return info, nil
|
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 {
|
type attachmentGetDeletePoster interface {
|
||||||
attachmentPoster
|
attachmentPoster
|
||||||
GetAttachments(
|
GetAttachments(
|
||||||
@ -346,9 +207,7 @@ type attachmentGetDeletePoster interface {
|
|||||||
// the id changes which makes the ids unusable. In this function, we
|
// the id changes which makes the ids unusable. In this function, we
|
||||||
// use the name and content bytes to detect the changes. This function
|
// use the name and content bytes to detect the changes. This function
|
||||||
// can be used to update the attachments of any event irrespective of
|
// can be used to update the attachments of any event irrespective of
|
||||||
// whether they are event instances of a series master although for
|
// whether they are event instances of a series master.
|
||||||
// newer event, since we probably won't already have any events it
|
|
||||||
// would be better use Post[Small|Large]Attachment.
|
|
||||||
func updateAttachments(
|
func updateAttachments(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
agdp attachmentGetDeletePoster,
|
agdp attachmentGetDeletePoster,
|
||||||
@ -445,70 +304,6 @@ func updateAttachments(
|
|||||||
return el.Failure()
|
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(
|
func (h eventRestoreHandler) getItemsInContainerByCollisionKey(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user