diff --git a/src/cmd/purge/purge.go b/src/cmd/purge/purge.go index 5a9c486fc..045fad44e 100644 --- a/src/cmd/purge/purge.go +++ b/src/cmd/purge/purge.go @@ -15,6 +15,7 @@ import ( "github.com/alcionai/corso/src/internal/connector" "github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/credentials" ) @@ -43,6 +44,12 @@ var contactsCmd = &cobra.Command{ RunE: handleContactsFolderPurge, } +var oneDriveCmd = &cobra.Command{ + Use: "onedrive", + Short: "Purges OneDrive folders", + RunE: handleOneDriveFolderPurge, +} + var ( before string user string @@ -69,6 +76,7 @@ func main() { purgeCmd.AddCommand(mailCmd) purgeCmd.AddCommand(eventsCmd) purgeCmd.AddCommand(contactsCmd) + purgeCmd.AddCommand(oneDriveCmd) if err := purgeCmd.ExecuteContext(ctx); err != nil { os.Exit(1) @@ -92,6 +100,7 @@ func handleAllFolderPurge(cmd *cobra.Command, args []string) error { purgeMailFolders, purgeCalendarFolders, purgeContactFolders, + purgeOneDriveFolders, ) if err != nil { return Only(ctx, ErrPurging) @@ -157,6 +166,25 @@ func handleContactsFolderPurge(cmd *cobra.Command, args []string) error { return nil } +func handleOneDriveFolderPurge(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + if utils.HasNoFlagsAndShownHelp(cmd) { + return nil + } + + gc, t, err := getGCAndBoundaryTime(ctx) + if err != nil { + return err + } + + if err := runPurgeForEachUser(ctx, gc, t, purgeOneDriveFolders); err != nil { + return Only(ctx, errors.Wrap(ErrPurging, "OneDrive folders")) + } + + return nil +} + // ------------------------------------------------------------------------------------------ // Purge Controllers // ------------------------------------------------------------------------------------------ @@ -212,8 +240,8 @@ func purgeMailFolders( return purgables, nil } - deleter := func(gs graph.Service, uid, fid string) error { - return exchange.DeleteMailFolder(ctx, gs, uid, fid) + deleter := func(gs graph.Service, uid string, f purgable) error { + return exchange.DeleteMailFolder(ctx, gs, uid, *f.GetId()) } return purgeFolders(ctx, gc, boundary, "Mail Folders", uid, getter, deleter) @@ -242,8 +270,8 @@ func purgeCalendarFolders( return purgables, nil } - deleter := func(gs graph.Service, uid, fid string) error { - return exchange.DeleteCalendar(ctx, gs, uid, fid) + deleter := func(gs graph.Service, uid string, f purgable) error { + return exchange.DeleteCalendar(ctx, gs, uid, *f.GetId()) } return purgeFolders(ctx, gc, boundary, "Event Calendars", uid, getter, deleter) @@ -272,13 +300,53 @@ func purgeContactFolders( return purgables, nil } - deleter := func(gs graph.Service, uid, fid string) error { - return exchange.DeleteContactFolder(ctx, gs, uid, fid) + deleter := func(gs graph.Service, uid string, f purgable) error { + return exchange.DeleteContactFolder(ctx, gs, uid, *f.GetId()) } return purgeFolders(ctx, gc, boundary, "Contact Folders", uid, getter, deleter) } +// ----- OneDrive + +func purgeOneDriveFolders( + ctx context.Context, + gc *connector.GraphConnector, + boundary time.Time, + uid string, +) error { + getter := func(gs graph.Service, uid, prefix string) ([]purgable, error) { + cfs, err := onedrive.GetAllFolders(ctx, gs, uid, prefix) + if err != nil { + return nil, err + } + + purgables := make([]purgable, len(cfs)) + + for i, v := range cfs { + purgables[i] = v + } + + return purgables, nil + } + + deleter := func(gs graph.Service, uid string, f purgable) error { + driveFolder, ok := f.(*onedrive.Displayable) + if !ok { + return errors.New("non-OneDrive item") + } + + return onedrive.DeleteItem( + ctx, + gs, + *driveFolder.GetParentReference().GetDriveId(), + *f.GetId(), + ) + } + + return purgeFolders(ctx, gc, boundary, "OneDrive Folders", uid, getter, deleter) +} + // ----- controller func purgeFolders( @@ -287,7 +355,7 @@ func purgeFolders( boundary time.Time, data, uid string, getter func(graph.Service, string, string) ([]purgable, error), - deleter func(graph.Service, string, string) error, + deleter func(graph.Service, string, purgable) error, ) error { Infof(ctx, "\nContainer: %s", data) @@ -324,7 +392,7 @@ func purgeFolders( Infof(ctx, "Deleting [%s]", displayName) - err = deleter(gc.Service(), uid, *fld.GetId()) + err = deleter(gc.Service(), uid, fld) if err != nil { err = errors.Wrapf(err, "!! Error") errs = multierror.Append(errs, err) diff --git a/src/internal/connector/onedrive/drive.go b/src/internal/connector/onedrive/drive.go index 5cd1967bb..4c1b9b45c 100644 --- a/src/internal/connector/onedrive/drive.go +++ b/src/internal/connector/onedrive/drive.go @@ -160,6 +160,14 @@ func newItem(name string, folder bool) models.DriveItemable { return itemToCreate } +type Displayable struct { + models.DriveItemable +} + +func (op *Displayable) GetDisplayName() *string { + return op.GetName() +} + // GetAllFolders returns all folders in all drives for the given user. If a // prefix is given, returns all folders with that prefix, regardless of if they // are a subfolder or top-level folder in the hierarchy. @@ -168,13 +176,13 @@ func GetAllFolders( gs graph.Service, userID string, prefix string, -) ([]models.DriveItemable, error) { +) ([]*Displayable, error) { drives, err := drives(ctx, gs, userID) if err != nil { return nil, errors.Wrap(err, "getting OneDrive folders") } - res := []models.DriveItemable{} + res := []*Displayable{} for _, d := range drives { err = collectItems( @@ -199,7 +207,7 @@ func GetAllFolders( // Add the item instead of the folder because the item has more // functionality. - res = append(res, item) + res = append(res, &Displayable{item}) } return nil diff --git a/src/internal/observe/observe_test.go b/src/internal/observe/observe_test.go index 7fd6e96c7..c7b7c7960 100644 --- a/src/internal/observe/observe_test.go +++ b/src/internal/observe/observe_test.go @@ -7,10 +7,11 @@ import ( "strings" "testing" - "github.com/alcionai/corso/src/internal/observe" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/observe" ) type ObserveProgressUnitSuite struct {