diff --git a/src/internal/m365/controller.go b/src/internal/m365/controller.go index b2ee92443..7b2ee6e7f 100644 --- a/src/internal/m365/controller.go +++ b/src/internal/m365/controller.go @@ -39,7 +39,7 @@ type Controller struct { tenant string credentials account.M365Config - ownerLookup idname.GetResourceIDAndNamer + resourceHandler idname.GetResourceIDAndNamer // maps of resource owner ids to names, and names to ids. // not guaranteed to be populated, only here as a post-population // reference for processes that choose to populate the values. @@ -83,43 +83,22 @@ func NewController( return nil, clues.Wrap(err, "creating api client").WithClues(ctx) } - var rCli *resourceClient - - // no failure for unknown service. - // In that case we create a controller that doesn't attempt to look up any resource - // data. This case helps avoid unnecessary service calls when the end user is running - // repo init and connect commands via the CLI. All other callers should be expected - // to pass in a known service, or else expect downstream failures. - if pst != path.UnknownService { - rc := resource.UnknownResource - - switch pst { - case path.ExchangeService, path.OneDriveService: - rc = resource.Users - case path.GroupsService: - rc = resource.Groups - case path.SharePointService: - rc = resource.Sites - } - - rCli, err = getResourceClient(rc, ac) - if err != nil { - return nil, clues.Wrap(err, "creating resource client").WithClues(ctx) - } - } - ctrl := Controller{ AC: ac, IDNameLookup: idname.NewCache(nil), credentials: creds, - ownerLookup: rCli, tenant: acct.ID(), wg: &sync.WaitGroup{}, backupDriveIDNames: idname.NewCache(nil), backupSiteIDWebURL: idname.NewCache(nil), } + // TODO: remove in favor of calling setResourceHandler in the newServiceHandler once + // all operations types are populated. When we do that, we can also remove pst from + // the func params in this call. + ctrl.setResourceHandler(pst) + return &ctrl, nil } @@ -127,6 +106,32 @@ func (ctrl *Controller) VerifyAccess(ctx context.Context) error { return ctrl.AC.Access().GetToken(ctx) } +func (ctrl *Controller) setResourceHandler( + serviceInOperation path.ServiceType, +) { + var rh *resourceGetter + + switch serviceInOperation { + case path.ExchangeService, path.OneDriveService: + rh = &resourceGetter{ + enum: resource.Users, + getter: ctrl.AC.Users(), + } + case path.GroupsService: + rh = &resourceGetter{ + enum: resource.Groups, + getter: ctrl.AC.Groups(), + } + case path.SharePointService: + rh = &resourceGetter{ + enum: resource.Sites, + getter: ctrl.AC.Sites(), + } + } + + ctrl.resourceHandler = rh +} + // --------------------------------------------------------------------------- // Processing Status // --------------------------------------------------------------------------- @@ -203,20 +208,9 @@ func (ctrl *Controller) CacheItemInfo(dii details.ItemInfo) { // Resource Lookup Handling // --------------------------------------------------------------------------- -func getResourceClient(rc resource.Category, ac api.Client) (*resourceClient, error) { - switch rc { - case resource.Users: - return &resourceClient{enum: rc, getter: ac.Users()}, nil - case resource.Sites: - return &resourceClient{enum: rc, getter: ac.Sites()}, nil - case resource.Groups: - return &resourceClient{enum: rc, getter: ac.Groups()}, nil - default: - return nil, clues.New("unrecognized owner resource type").With("resource_enum", rc) - } -} +var _ idname.GetResourceIDAndNamer = &resourceGetter{} -type resourceClient struct { +type resourceGetter struct { enum resource.Category getter getIDAndNamer } @@ -233,15 +227,13 @@ type getIDAndNamer interface { ) } -var _ idname.GetResourceIDAndNamer = &resourceClient{} - // GetResourceIDAndNameFrom looks up the resource's canonical id and display name. // If the resource is present in the idNameSwapper, then that interface's id and // name values are returned. As a fallback, the resource calls the discovery // api to fetch the user or site using the resource value. This fallback assumes // that the resource is a well formed ID or display name of appropriate design // (PrincipalName for users, WebURL for sites). -func (r resourceClient) GetResourceIDAndNameFrom( +func (r resourceGetter) GetResourceIDAndNameFrom( ctx context.Context, owner string, ins idname.Cacher, @@ -294,11 +286,11 @@ func (ctrl *Controller) PopulateProtectedResourceIDAndName( resourceID string, // input value, can be either id or name ins idname.Cacher, ) (idname.Provider, error) { - if ctrl.ownerLookup == nil { + if ctrl.resourceHandler == nil { return nil, clues.Stack(ErrNoResourceLookup).WithClues(ctx) } - pr, err := ctrl.ownerLookup.GetResourceIDAndNameFrom(ctx, resourceID, ins) + pr, err := ctrl.resourceHandler.GetResourceIDAndNameFrom(ctx, resourceID, ins) if err != nil { return nil, clues.Wrap(err, "identifying resource owner") } diff --git a/src/internal/m365/controller_test.go b/src/internal/m365/controller_test.go index d06b9ce3b..f6074067d 100644 --- a/src/internal/m365/controller_test.go +++ b/src/internal/m365/controller_test.go @@ -57,18 +57,18 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() { var ( itn = map[string]string{id: name} nti = map[string]string{name: id} - lookup = &resourceClient{ + lookup = &resourceGetter{ enum: resource.Users, getter: &mock.IDNameGetter{ID: id, Name: name}, } - noLookup = &resourceClient{enum: resource.Users, getter: &mock.IDNameGetter{}} + noLookup = &resourceGetter{enum: resource.Users, getter: &mock.IDNameGetter{}} ) table := []struct { name string protectedResource string ins inMock.Cache - rc *resourceClient + rc *resourceGetter expectID string expectName string expectErr require.ErrorAssertionFunc @@ -238,7 +238,7 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() { ctx, flush := tester.NewContext(t) defer flush() - ctrl := &Controller{ownerLookup: test.rc} + ctrl := &Controller{resourceHandler: test.rc} resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.protectedResource, test.ins) test.expectErr(t, err, clues.ToCore(err)) @@ -260,7 +260,7 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom_nilCheck() { ctx, flush := tester.NewContext(t) defer flush() - ctrl := &Controller{ownerLookup: nil} + ctrl := &Controller{resourceHandler: nil} _, err := ctrl.PopulateProtectedResourceIDAndName(ctx, "", nil) require.ErrorIs(t, err, ErrNoResourceLookup, clues.ToCore(err)) diff --git a/src/internal/m365/export.go b/src/internal/m365/export.go index c7136f221..2c21d5363 100644 --- a/src/internal/m365/export.go +++ b/src/internal/m365/export.go @@ -17,6 +17,8 @@ func (ctrl *Controller) NewServiceHandler( opts control.Options, service path.ServiceType, ) (inject.ServiceHandler, error) { + ctrl.setResourceHandler(service) + switch service { case path.OneDriveService: return onedrive.NewOneDriveHandler(opts), nil diff --git a/src/internal/operations/test/helper_test.go b/src/internal/operations/test/helper_test.go index 1dd34e01b..beb25dfb9 100644 --- a/src/internal/operations/test/helper_test.go +++ b/src/internal/operations/test/helper_test.go @@ -570,6 +570,8 @@ func ControllerWithSelector( onFail func(*testing.T, context.Context), counter *count.Bus, ) (*m365.Controller, selectors.Selector) { + ctx = clues.Add(ctx, "controller_selector", sel) + ctrl, err := m365.NewController( ctx, acct,