add a resource getter setter to newServiceHandler (#4666)

This sets us up to remove the service declaration
from the two-step repo creation + init/config, and allows sdk consumers to return to ignoring service until acting on the operation.
---

replacement branch for: https://github.com/alcionai/corso/pull/4494
This commit is contained in:
Keepers 2023-11-13 10:49:53 -07:00 committed by GitHub
parent b14b7267fe
commit 2ccd7da6a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 46 additions and 50 deletions

View File

@ -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")
}

View File

@ -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))

View File

@ -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

View File

@ -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,