add teamschats restore shim for export support
This commit is contained in:
parent
b9f71280bf
commit
a7679554ab
100
src/internal/m365/service/teamschats/restore.go
Normal file
100
src/internal/m365/service/teamschats/restore.go
Normal file
@ -0,0 +1,100 @@
|
||||
package teamschats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/data"
|
||||
"github.com/alcionai/corso/src/internal/m365/support"
|
||||
"github.com/alcionai/corso/src/internal/operations/inject"
|
||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||
"github.com/alcionai/corso/src/pkg/count"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/logger"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/services/m365/api/graph"
|
||||
)
|
||||
|
||||
// ConsumeRestoreCollections will restore the specified data collections
|
||||
func (h *teamsChatsHandler) ConsumeRestoreCollections(
|
||||
ctx context.Context,
|
||||
rcc inject.RestoreConsumerConfig,
|
||||
dcs []data.RestoreCollection,
|
||||
errs *fault.Bus,
|
||||
ctr *count.Bus,
|
||||
) (*details.Details, *data.CollectionStats, error) {
|
||||
if len(dcs) == 0 {
|
||||
return nil, nil, clues.WrapWC(ctx, data.ErrNoData, "performing restore")
|
||||
}
|
||||
|
||||
// TODO(ashmrtn): We should stop relying on the context for rate limiter stuff
|
||||
// and instead configure this when we make the handler instance. We can't
|
||||
// initialize it in the NewHandler call right now because those functions
|
||||
// aren't (and shouldn't be) returning a context along with the handler. Since
|
||||
// that call isn't directly calling into this function even if we did
|
||||
// initialize the rate limiter there it would be lost because it wouldn't get
|
||||
// stored in an ancestor of the context passed to this function.
|
||||
ctx = graph.BindRateLimiterConfig(
|
||||
ctx,
|
||||
graph.LimiterCfg{Service: path.TeamsChatsService})
|
||||
|
||||
var (
|
||||
deets = &details.Builder{}
|
||||
restoreMetrics support.CollectionMetrics
|
||||
el = errs.Local()
|
||||
)
|
||||
|
||||
// Reorder collections so that the parents directories are created
|
||||
// before the child directories; a requirement for permissions.
|
||||
data.SortRestoreCollections(dcs)
|
||||
|
||||
// Iterate through the data collections and restore the contents of each
|
||||
for _, dc := range dcs {
|
||||
if el.Failure() != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
category = dc.FullPath().Category()
|
||||
metrics support.CollectionMetrics
|
||||
ictx = clues.Add(ctx,
|
||||
"category", category,
|
||||
"restore_location", clues.Hide(rcc.RestoreConfig.Location),
|
||||
"protected_resource", clues.Hide(dc.FullPath().ProtectedResource()),
|
||||
"full_path", dc.FullPath())
|
||||
)
|
||||
|
||||
switch dc.FullPath().Category() {
|
||||
case path.ChatsCategory:
|
||||
// chats cannot be restored using Graph API.
|
||||
// a delegated token is required, and Corso has no
|
||||
// good way of obtaining such a token.
|
||||
logger.Ctx(ictx).Debug("Skipping restore for channel messages")
|
||||
default:
|
||||
return nil, nil, clues.NewWC(ictx, "data category not supported").
|
||||
With("category", category)
|
||||
}
|
||||
|
||||
restoreMetrics = support.CombineMetrics(restoreMetrics, metrics)
|
||||
|
||||
if err != nil {
|
||||
el.AddRecoverable(ictx, err)
|
||||
}
|
||||
|
||||
if errors.Is(err, context.Canceled) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
status := support.CreateStatus(
|
||||
ctx,
|
||||
support.Restore,
|
||||
len(dcs),
|
||||
restoreMetrics,
|
||||
rcc.RestoreConfig.Location)
|
||||
|
||||
return deets.Details(), status.ToCollectionStats(), el.Failure()
|
||||
}
|
||||
54
src/internal/m365/service/teamschats/restore_test.go
Normal file
54
src/internal/m365/service/teamschats/restore_test.go
Normal file
@ -0,0 +1,54 @@
|
||||
package teamschats
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alcionai/clues"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/alcionai/corso/src/internal/data"
|
||||
"github.com/alcionai/corso/src/internal/data/mock"
|
||||
"github.com/alcionai/corso/src/internal/operations/inject"
|
||||
"github.com/alcionai/corso/src/internal/tester"
|
||||
"github.com/alcionai/corso/src/pkg/fault"
|
||||
"github.com/alcionai/corso/src/pkg/path"
|
||||
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||
)
|
||||
|
||||
type RestoreUnitSuite struct {
|
||||
tester.Suite
|
||||
}
|
||||
|
||||
func TestRestoreUnitSuite(t *testing.T) {
|
||||
suite.Run(t, &RestoreUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||
}
|
||||
|
||||
func (suite *RestoreUnitSuite) TestConsumeRestoreCollections_noErrorOnChats() {
|
||||
t := suite.T()
|
||||
|
||||
ctx, flush := tester.NewContext(t)
|
||||
defer flush()
|
||||
|
||||
rcc := inject.RestoreConsumerConfig{}
|
||||
pth, err := path.BuildPrefix(
|
||||
"t",
|
||||
"pr",
|
||||
path.TeamsChatsService,
|
||||
path.ChatsCategory)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
dcs := []data.RestoreCollection{
|
||||
mock.Collection{Path: pth},
|
||||
}
|
||||
|
||||
_, _, err = NewTeamsChatsHandler(api.Client{}, nil).
|
||||
ConsumeRestoreCollections(
|
||||
ctx,
|
||||
rcc,
|
||||
dcs,
|
||||
fault.New(false),
|
||||
nil)
|
||||
assert.NoError(t, err, "Chats restore")
|
||||
}
|
||||
@ -142,6 +142,7 @@ func makeRestorePathsForEntry(
|
||||
// * OneDrive/SharePoint (needs drive information)
|
||||
switch true {
|
||||
case ent.Exchange != nil ||
|
||||
ent.TeamsChats != nil ||
|
||||
(ent.Groups != nil && ent.Groups.ItemType == details.GroupsChannelMessage) ||
|
||||
(ent.SharePoint != nil && ent.SharePoint.ItemType == details.SharePointList):
|
||||
// TODO(ashmrtn): Eventually make Events have it's own function to handle
|
||||
|
||||
@ -399,6 +399,30 @@ func (suite *RestorePathTransformerUnitSuite) TestGetPaths() {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "TeamsChats Chats",
|
||||
backupVersion: version.Groups9Update,
|
||||
input: []*details.Entry{
|
||||
{
|
||||
RepoRef: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||
LocationRef: testdata.ExchangeEmailItemPath3.Loc.String(),
|
||||
ItemInfo: details.ItemInfo{
|
||||
Exchange: &details.ExchangeInfo{
|
||||
ItemType: details.ExchangeMail,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectErr: assert.NoError,
|
||||
expected: []expectPaths{
|
||||
{
|
||||
storage: testdata.ExchangeEmailItemPath3.RR.String(),
|
||||
restore: toRestore(
|
||||
testdata.ExchangeEmailItemPath3.RR,
|
||||
testdata.ExchangeEmailItemPath3.Loc.Elements()...),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user