Allow purge command to delete OneDrive folders (#1094)

* Add functions for OneDrive purge

function is setup to return and take purgable instead of just userID,
itemID for deleter.

* Adjust other functions to take purgable

Adjust interface for deleter so that it allows more information to the
delete function. The called function can cast the purgable it was passed
back to the type that was returned by the getter function.

* Move struct definition

Per reviewer comment move struct definition to OneDrive package instead
of leaving in purge command.

* Fix lint error

Likely slipped through while GitHub actions was having issues.
This commit is contained in:
ashmrtn 2022-10-10 18:16:46 -07:00 committed by GitHub
parent 2de54a920c
commit f4befaf849
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 12 deletions

View File

@ -15,6 +15,7 @@ import (
"github.com/alcionai/corso/src/internal/connector" "github.com/alcionai/corso/src/internal/connector"
"github.com/alcionai/corso/src/internal/connector/exchange" "github.com/alcionai/corso/src/internal/connector/exchange"
"github.com/alcionai/corso/src/internal/connector/graph" "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/account"
"github.com/alcionai/corso/src/pkg/credentials" "github.com/alcionai/corso/src/pkg/credentials"
) )
@ -43,6 +44,12 @@ var contactsCmd = &cobra.Command{
RunE: handleContactsFolderPurge, RunE: handleContactsFolderPurge,
} }
var oneDriveCmd = &cobra.Command{
Use: "onedrive",
Short: "Purges OneDrive folders",
RunE: handleOneDriveFolderPurge,
}
var ( var (
before string before string
user string user string
@ -69,6 +76,7 @@ func main() {
purgeCmd.AddCommand(mailCmd) purgeCmd.AddCommand(mailCmd)
purgeCmd.AddCommand(eventsCmd) purgeCmd.AddCommand(eventsCmd)
purgeCmd.AddCommand(contactsCmd) purgeCmd.AddCommand(contactsCmd)
purgeCmd.AddCommand(oneDriveCmd)
if err := purgeCmd.ExecuteContext(ctx); err != nil { if err := purgeCmd.ExecuteContext(ctx); err != nil {
os.Exit(1) os.Exit(1)
@ -92,6 +100,7 @@ func handleAllFolderPurge(cmd *cobra.Command, args []string) error {
purgeMailFolders, purgeMailFolders,
purgeCalendarFolders, purgeCalendarFolders,
purgeContactFolders, purgeContactFolders,
purgeOneDriveFolders,
) )
if err != nil { if err != nil {
return Only(ctx, ErrPurging) return Only(ctx, ErrPurging)
@ -157,6 +166,25 @@ func handleContactsFolderPurge(cmd *cobra.Command, args []string) error {
return nil 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 // Purge Controllers
// ------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------
@ -212,8 +240,8 @@ func purgeMailFolders(
return purgables, nil return purgables, nil
} }
deleter := func(gs graph.Service, uid, fid string) error { deleter := func(gs graph.Service, uid string, f purgable) error {
return exchange.DeleteMailFolder(ctx, gs, uid, fid) return exchange.DeleteMailFolder(ctx, gs, uid, *f.GetId())
} }
return purgeFolders(ctx, gc, boundary, "Mail Folders", uid, getter, deleter) return purgeFolders(ctx, gc, boundary, "Mail Folders", uid, getter, deleter)
@ -242,8 +270,8 @@ func purgeCalendarFolders(
return purgables, nil return purgables, nil
} }
deleter := func(gs graph.Service, uid, fid string) error { deleter := func(gs graph.Service, uid string, f purgable) error {
return exchange.DeleteCalendar(ctx, gs, uid, fid) return exchange.DeleteCalendar(ctx, gs, uid, *f.GetId())
} }
return purgeFolders(ctx, gc, boundary, "Event Calendars", uid, getter, deleter) return purgeFolders(ctx, gc, boundary, "Event Calendars", uid, getter, deleter)
@ -272,13 +300,53 @@ func purgeContactFolders(
return purgables, nil return purgables, nil
} }
deleter := func(gs graph.Service, uid, fid string) error { deleter := func(gs graph.Service, uid string, f purgable) error {
return exchange.DeleteContactFolder(ctx, gs, uid, fid) return exchange.DeleteContactFolder(ctx, gs, uid, *f.GetId())
} }
return purgeFolders(ctx, gc, boundary, "Contact Folders", uid, getter, deleter) 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 // ----- controller
func purgeFolders( func purgeFolders(
@ -287,7 +355,7 @@ func purgeFolders(
boundary time.Time, boundary time.Time,
data, uid string, data, uid string,
getter func(graph.Service, string, string) ([]purgable, error), getter func(graph.Service, string, string) ([]purgable, error),
deleter func(graph.Service, string, string) error, deleter func(graph.Service, string, purgable) error,
) error { ) error {
Infof(ctx, "\nContainer: %s", data) Infof(ctx, "\nContainer: %s", data)
@ -324,7 +392,7 @@ func purgeFolders(
Infof(ctx, "Deleting [%s]", displayName) Infof(ctx, "Deleting [%s]", displayName)
err = deleter(gc.Service(), uid, *fld.GetId()) err = deleter(gc.Service(), uid, fld)
if err != nil { if err != nil {
err = errors.Wrapf(err, "!! Error") err = errors.Wrapf(err, "!! Error")
errs = multierror.Append(errs, err) errs = multierror.Append(errs, err)

View File

@ -160,6 +160,14 @@ func newItem(name string, folder bool) models.DriveItemable {
return itemToCreate 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 // 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 // prefix is given, returns all folders with that prefix, regardless of if they
// are a subfolder or top-level folder in the hierarchy. // are a subfolder or top-level folder in the hierarchy.
@ -168,13 +176,13 @@ func GetAllFolders(
gs graph.Service, gs graph.Service,
userID string, userID string,
prefix string, prefix string,
) ([]models.DriveItemable, error) { ) ([]*Displayable, error) {
drives, err := drives(ctx, gs, userID) drives, err := drives(ctx, gs, userID)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "getting OneDrive folders") return nil, errors.Wrap(err, "getting OneDrive folders")
} }
res := []models.DriveItemable{} res := []*Displayable{}
for _, d := range drives { for _, d := range drives {
err = collectItems( err = collectItems(
@ -199,7 +207,7 @@ func GetAllFolders(
// Add the item instead of the folder because the item has more // Add the item instead of the folder because the item has more
// functionality. // functionality.
res = append(res, item) res = append(res, &Displayable{item})
} }
return nil return nil

View File

@ -7,10 +7,11 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/alcionai/corso/src/internal/observe"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/observe"
) )
type ObserveProgressUnitSuite struct { type ObserveProgressUnitSuite struct {