add a resource getter setter to newServiceHandler

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.
This commit is contained in:
ryanfkeepers 2023-10-12 20:11:33 -06:00
parent ea308055eb
commit 5a50627386
3 changed files with 43 additions and 49 deletions

View File

@ -39,7 +39,7 @@ type Controller struct {
tenant string tenant string
credentials account.M365Config credentials account.M365Config
ownerLookup idname.GetResourceIDAndNamer resourceHandler idname.GetResourceIDAndNamer
// maps of resource owner ids to names, and names to ids. // maps of resource owner ids to names, and names to ids.
// not guaranteed to be populated, only here as a post-population // not guaranteed to be populated, only here as a post-population
// reference for processes that choose to populate the values. // 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) 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{ ctrl := Controller{
AC: ac, AC: ac,
IDNameLookup: idname.NewCache(nil), IDNameLookup: idname.NewCache(nil),
credentials: creds, credentials: creds,
ownerLookup: rCli,
tenant: acct.ID(), tenant: acct.ID(),
wg: &sync.WaitGroup{}, wg: &sync.WaitGroup{},
backupDriveIDNames: idname.NewCache(nil), backupDriveIDNames: idname.NewCache(nil),
backupSiteIDWebURL: 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 return &ctrl, nil
} }
@ -127,6 +106,32 @@ func (ctrl *Controller) VerifyAccess(ctx context.Context) error {
return ctrl.AC.Access().GetToken(ctx) 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.Sites,
getter: ctrl.AC.Sites(),
}
case path.SharePointService:
rh = &resourceGetter{
enum: resource.Groups,
getter: ctrl.AC.Groups(),
}
}
ctrl.resourceHandler = rh
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Processing Status // Processing Status
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -203,20 +208,9 @@ func (ctrl *Controller) CacheItemInfo(dii details.ItemInfo) {
// Resource Lookup Handling // Resource Lookup Handling
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func getResourceClient(rc resource.Category, ac api.Client) (*resourceClient, error) { var _ idname.GetResourceIDAndNamer = &resourceGetter{}
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)
}
}
type resourceClient struct { type resourceGetter struct {
enum resource.Category enum resource.Category
getter getIDAndNamer getter getIDAndNamer
} }
@ -233,15 +227,13 @@ type getIDAndNamer interface {
) )
} }
var _ idname.GetResourceIDAndNamer = &resourceClient{}
// GetResourceIDAndNameFrom looks up the resource's canonical id and display name. // 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 // 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 // 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 // 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 // that the resource is a well formed ID or display name of appropriate design
// (PrincipalName for users, WebURL for sites). // (PrincipalName for users, WebURL for sites).
func (r resourceClient) GetResourceIDAndNameFrom( func (r resourceGetter) GetResourceIDAndNameFrom(
ctx context.Context, ctx context.Context,
owner string, owner string,
ins idname.Cacher, ins idname.Cacher,
@ -294,11 +286,11 @@ func (ctrl *Controller) PopulateProtectedResourceIDAndName(
resourceID string, // input value, can be either id or name resourceID string, // input value, can be either id or name
ins idname.Cacher, ins idname.Cacher,
) (idname.Provider, error) { ) (idname.Provider, error) {
if ctrl.ownerLookup == nil { if ctrl.resourceHandler == nil {
return nil, clues.Stack(ErrNoResourceLookup).WithClues(ctx) 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 { if err != nil {
return nil, clues.Wrap(err, "identifying resource owner") return nil, clues.Wrap(err, "identifying resource owner")
} }

View File

@ -57,18 +57,18 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
var ( var (
itn = map[string]string{id: name} itn = map[string]string{id: name}
nti = map[string]string{name: id} nti = map[string]string{name: id}
lookup = &resourceClient{ lookup = &resourceGetter{
enum: resource.Users, enum: resource.Users,
getter: &mock.IDNameGetter{ID: id, Name: name}, 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 { table := []struct {
name string name string
protectedResource string protectedResource string
ins inMock.Cache ins inMock.Cache
rc *resourceClient rc *resourceGetter
expectID string expectID string
expectName string expectName string
expectErr require.ErrorAssertionFunc expectErr require.ErrorAssertionFunc
@ -238,7 +238,7 @@ func (suite *ControllerUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
ctx, flush := tester.NewContext(t) ctx, flush := tester.NewContext(t)
defer flush() defer flush()
ctrl := &Controller{ownerLookup: test.rc} ctrl := &Controller{resourceHandler: test.rc}
resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.protectedResource, test.ins) resource, err := ctrl.PopulateProtectedResourceIDAndName(ctx, test.protectedResource, test.ins)
test.expectErr(t, err, clues.ToCore(err)) test.expectErr(t, err, clues.ToCore(err))

View File

@ -17,6 +17,8 @@ func (ctrl *Controller) NewServiceHandler(
opts control.Options, opts control.Options,
service path.ServiceType, service path.ServiceType,
) (inject.ServiceHandler, error) { ) (inject.ServiceHandler, error) {
ctrl.setResourceHandler(service)
switch service { switch service {
case path.OneDriveService: case path.OneDriveService:
return onedrive.NewOneDriveHandler(opts), nil return onedrive.NewOneDriveHandler(opts), nil