diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index def430f14..5ef6ef6be 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -293,7 +293,7 @@ func (gc *GraphConnector) RestoreDataCollections( case selectors.ServiceOneDrive: status, err = onedrive.RestoreCollections(ctx, backupVersion, gc.Service, dest, opts, dcs, deets) case selectors.ServiceSharePoint: - status, err = sharepoint.RestoreCollections(ctx, backupVersion, gc.Service, dest, dcs, deets) + status, err = sharepoint.RestoreCollections(ctx, backupVersion, creds, gc.Service, dest, dcs, deets) default: err = errors.Errorf("restore data from service %s not supported", selector.Service.String()) } diff --git a/src/internal/connector/sharepoint/restore.go b/src/internal/connector/sharepoint/restore.go index 3cf35d287..10cf125e7 100644 --- a/src/internal/connector/sharepoint/restore.go +++ b/src/internal/connector/sharepoint/restore.go @@ -9,11 +9,14 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/pkg/errors" + discover "github.com/alcionai/corso/src/internal/connector/discovery/api" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/onedrive" + "github.com/alcionai/corso/src/internal/connector/sharepoint/api" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/data" D "github.com/alcionai/corso/src/internal/diagnostics" + "github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/logger" @@ -37,6 +40,7 @@ import ( func RestoreCollections( ctx context.Context, backupVersion int, + creds account.M365Config, service graph.Servicer, dest control.RestoreDestination, dcs []data.Collection, @@ -74,7 +78,7 @@ func RestoreCollections( false, ) case path.ListsCategory: - metrics, canceled = RestoreCollection( + metrics, canceled = RestoreListCollection( ctx, service, dc, @@ -83,11 +87,14 @@ func RestoreCollections( errUpdater, ) case path.PagesCategory: - errorMessage := fmt.Sprintf("restore of %s not supported", dc.FullPath().Category()) - logger.Ctx(ctx).Error(errorMessage) - - return nil, errors.New(errorMessage) - + metrics, canceled = RestorePageCollection( + ctx, + creds, + dc, + dest.ContainerName, + deets, + errUpdater, + ) default: return nil, errors.Errorf("category %s not supported", dc.FullPath().Category()) } @@ -209,7 +216,7 @@ func restoreListItem( return dii, nil } -func RestoreCollection( +func RestoreListCollection( ctx context.Context, service graph.Servicer, dc data.Collection, @@ -217,7 +224,7 @@ func RestoreCollection( deets *details.Builder, errUpdater func(string, error), ) (support.CollectionMetrics, bool) { - ctx, end := D.Span(ctx, "gc:sharepoint:restoreCollection", D.Label("path", dc.FullPath())) + ctx, end := D.Span(ctx, "gc:sharepoint:restoreListCollection", D.Label("path", dc.FullPath())) defer end() var ( @@ -225,7 +232,7 @@ func RestoreCollection( directory = dc.FullPath() ) - trace.Log(ctx, "gc:sharepoint:restoreCollection", directory.String()) + trace.Log(ctx, "gc:sharepoint:restoreListCollection", directory.String()) siteID := directory.ResourceOwner() // Restore items from the collection @@ -276,3 +283,83 @@ func RestoreCollection( } } } + +// RestorePageCollection handles restoration of an individual site page collection. +// returns: +// - the collection's item and byte count metrics +// - the context cancellation station. True iff context is canceled. +func RestorePageCollection( + ctx context.Context, + creds account.M365Config, + dc data.Collection, + restoreContainerName string, + deets *details.Builder, + errUpdater func(string, error), +) (support.CollectionMetrics, bool) { + ctx, end := D.Span(ctx, "gc:sharepoint:restorePageCollection", D.Label("path", dc.FullPath())) + defer end() + + var ( + metrics = support.CollectionMetrics{} + directory = dc.FullPath() + ) + + adpt, err := graph.CreateAdapter(creds.AzureTenantID, creds.AzureClientID, creds.AzureClientSecret) + if err != nil { + return metrics, false + } + + service := discover.NewBetaService(adpt) + + trace.Log(ctx, "gc:sharepoint:restorePageCollection", directory.String()) + siteID := directory.ResourceOwner() + + // Restore items from collection + items := dc.Items() + + for { + select { + case <-ctx.Done(): + errUpdater("context canceled", ctx.Err()) + return metrics, true + + case itemData, ok := <-items: + if !ok { + return metrics, false + } + metrics.Objects++ + + itemInfo, err := api.RestoreSitePage( + ctx, + service, + itemData, + siteID, + restoreContainerName, + ) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + metrics.TotalBytes += itemInfo.SharePoint.Size + + itemPath, err := dc.FullPath().Append(itemData.UUID(), true) + if err != nil { + logger.Ctx(ctx).Errorw("transforming item to full path", "error", err) + errUpdater(itemData.UUID(), err) + + continue + } + + deets.Add( + itemPath.String(), + itemPath.ShortRef(), + "", + true, + itemInfo, + ) + + metrics.Successes++ + } + } +}