Compare commits

...

1 Commits

Author SHA1 Message Date
ryanfkeepers
135c638324 centralize contact location in cache resolver
moves all handling of the "contacts" root folder presence into
the contacts container cache.  This ensures we can
remove any one-off code specifically targeting contacts'
peculiar root folder handling out of generic code paths and into
a data-specific owner.

The addition of a "defaultRootLocation" api is necessary otherwise
the cache lookup on restore always fails (or worse, gets confused
by the well known folder and an identically named subfolder),
and we end up with 409 responses and other issues.
2023-09-01 15:56:03 -06:00
8 changed files with 51 additions and 40 deletions

View File

@ -91,12 +91,10 @@ func (mg mockGetter) GetAddedAndRemovedItemIDs(
var _ graph.ContainerResolver = &mockResolver{} var _ graph.ContainerResolver = &mockResolver{}
type ( type mockResolver struct {
mockResolver struct { items []graph.CachedContainer
items []graph.CachedContainer added map[string]string
added map[string]string }
}
)
func newMockResolver(items ...mockContainer) mockResolver { func newMockResolver(items ...mockContainer) mockResolver {
is := make([]graph.CachedContainer, 0, len(items)) is := make([]graph.CachedContainer, 0, len(items))
@ -125,9 +123,10 @@ func (m mockResolver) DestinationNameToID(dest string) string { return m.added[d
func (m mockResolver) IDToPath(context.Context, string) (*path.Builder, *path.Builder, error) { func (m mockResolver) IDToPath(context.Context, string) (*path.Builder, *path.Builder, error) {
return nil, nil, nil return nil, nil, nil
} }
func (m mockResolver) PathInCache(string) (string, bool) { return "", false } func (m mockResolver) PathInCache(string) (string, bool) { return "", false }
func (m mockResolver) LocationInCache(string) (string, bool) { return "", false } func (m mockResolver) LocationInCache(string) (string, bool) { return "", false }
func (m mockResolver) Populate(context.Context, *fault.Bus, string, ...string) error { return nil } func (m mockResolver) Populate(context.Context, *fault.Bus, string) error { return nil }
func (m mockResolver) DefaultRootLocation() string { return "" }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Unit tests // Unit tests

View File

@ -37,25 +37,27 @@ func (r *contactRefresher) refreshContainer(
type contactContainerCache struct { type contactContainerCache struct {
*containerResolver *containerResolver
enumer containersEnumerator enumer containersEnumerator
getter containerGetter getter containerGetter
userID string userID string
rootLocation string
} }
func (cfc *contactContainerCache) populateContactRoot( func (cfc *contactContainerCache) populateContactRoot(
ctx context.Context, ctx context.Context,
directoryID string, directoryID string,
baseContainerPath []string,
) error { ) error {
f, err := cfc.getter.GetContainerByID(ctx, cfc.userID, directoryID) f, err := cfc.getter.GetContainerByID(ctx, cfc.userID, directoryID)
if err != nil { if err != nil {
return clues.Wrap(err, "fetching root folder") return clues.Wrap(err, "fetching root folder")
} }
cfc.rootLocation = ptr.Val(f.GetDisplayName())
temp := graph.NewCacheFolder( temp := graph.NewCacheFolder(
f, f,
path.Builder{}.Append(ptr.Val(f.GetId())), // path of IDs path.Builder{}.Append(ptr.Val(f.GetId())), // path of IDs
path.Builder{}.Append(baseContainerPath...)) // display location path.Builder{}.Append(cfc.rootLocation)) // display location
if err := cfc.addFolder(&temp); err != nil { if err := cfc.addFolder(&temp); err != nil {
return clues.Wrap(err, "adding resolver dir").WithClues(ctx) return clues.Wrap(err, "adding resolver dir").WithClues(ctx)
} }
@ -71,9 +73,8 @@ func (cfc *contactContainerCache) Populate(
ctx context.Context, ctx context.Context,
errs *fault.Bus, errs *fault.Bus,
baseID string, baseID string,
baseContainerPath ...string,
) error { ) error {
if err := cfc.init(ctx, baseID, baseContainerPath); err != nil { if err := cfc.init(ctx, baseID); err != nil {
return clues.Wrap(err, "initializing") return clues.Wrap(err, "initializing")
} }
@ -92,7 +93,6 @@ func (cfc *contactContainerCache) Populate(
func (cfc *contactContainerCache) init( func (cfc *contactContainerCache) init(
ctx context.Context, ctx context.Context,
baseNode string, baseNode string,
baseContainerPath []string,
) error { ) error {
if len(baseNode) == 0 { if len(baseNode) == 0 {
return clues.New("m365 folderID required for base contact folder").WithClues(ctx) return clues.New("m365 folderID required for base contact folder").WithClues(ctx)
@ -105,5 +105,9 @@ func (cfc *contactContainerCache) init(
}) })
} }
return cfc.populateContactRoot(ctx, baseNode, baseContainerPath) return cfc.populateContactRoot(ctx, baseNode)
}
func (cfc *contactContainerCache) DefaultRootLocation() string {
return cfc.rootLocation
} }

View File

@ -719,16 +719,15 @@ func (suite *ContainerResolverSuite) TestPopulate() {
} }
tests := []struct { tests := []struct {
name, folderInCache, root, basePath string name, folderInCache, root string
resolverFunc func(t *testing.T) graph.ContainerResolver resolverFunc func(t *testing.T) graph.ContainerResolver
canFind assert.BoolAssertionFunc canFind assert.BoolAssertionFunc
}{ }{
{ {
name: "Default Event Cache", name: "Default Event Cache",
// Fine as long as this isn't running against a migrated Exchange server. // Fine as long as this isn't running against a migrated Exchange server.
folderInCache: api.DefaultCalendar, folderInCache: api.DefaultCalendar,
root: api.DefaultCalendar, root: api.DefaultCalendar,
basePath: api.DefaultCalendar,
resolverFunc: eventFunc, resolverFunc: eventFunc,
canFind: assert.True, canFind: assert.True,
}, },
@ -750,7 +749,6 @@ func (suite *ContainerResolverSuite) TestPopulate() {
name: "Default Contact Cache", name: "Default Contact Cache",
folderInCache: api.DefaultContacts, folderInCache: api.DefaultContacts,
root: api.DefaultContacts, root: api.DefaultContacts,
basePath: api.DefaultContacts,
canFind: assert.True, canFind: assert.True,
resolverFunc: contactFunc, resolverFunc: contactFunc,
}, },
@ -778,7 +776,7 @@ func (suite *ContainerResolverSuite) TestPopulate() {
resolver := test.resolverFunc(t) resolver := test.resolverFunc(t)
err := resolver.Populate(ctx, fault.New(true), test.root, test.basePath) err := resolver.Populate(ctx, fault.New(true), test.root)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
_, isFound := resolver.LocationInCache(test.folderInCache) _, isFound := resolver.LocationInCache(test.folderInCache)

View File

@ -64,7 +64,6 @@ func (ecc *eventContainerCache) Populate(
ctx context.Context, ctx context.Context,
errs *fault.Bus, errs *fault.Bus,
baseID string, baseID string,
baseContainerPath ...string,
) error { ) error {
if err := ecc.init(ctx); err != nil { if err := ecc.init(ctx); err != nil {
return clues.Wrap(err, "initializing") return clues.Wrap(err, "initializing")
@ -112,3 +111,7 @@ func (ecc *eventContainerCache) AddToCache(ctx context.Context, f graph.Containe
return nil return nil
} }
func (ecc *eventContainerCache) DefaultRootLocation() string {
return ""
}

View File

@ -94,7 +94,6 @@ func (mc *mailContainerCache) Populate(
ctx context.Context, ctx context.Context,
errs *fault.Bus, errs *fault.Bus,
baseID string, baseID string,
baseContainerPath ...string,
) error { ) error {
if err := mc.init(ctx); err != nil { if err := mc.init(ctx); err != nil {
return clues.Wrap(err, "initializing") return clues.Wrap(err, "initializing")
@ -111,3 +110,7 @@ func (mc *mailContainerCache) Populate(
return nil return nil
} }
func (mc *mailContainerCache) DefaultRootLocation() string {
return ""
}

View File

@ -69,11 +69,6 @@ func (suite *MailFolderCacheIntegrationSuite) TestDeltaFetch() {
name: "Node Root", name: "Node Root",
root: topFolderID, root: topFolderID,
}, },
{
name: "Node Root Non-empty Path",
root: topFolderID,
path: []string{"some", "leading", "path"},
},
} }
userID := tconfig.M365UserID(suite.T()) userID := tconfig.M365UserID(suite.T())
@ -95,7 +90,7 @@ func (suite *MailFolderCacheIntegrationSuite) TestDeltaFetch() {
getter: acm, getter: acm,
} }
err = mfc.Populate(ctx, fault.New(true), test.root, test.path...) err = mfc.Populate(ctx, fault.New(true), test.root)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
p, l, err := mfc.IDToPath(ctx, testFolderID) p, l, err := mfc.IDToPath(ctx, testFolderID)

View File

@ -135,11 +135,15 @@ func CreateDestination(
errs *fault.Bus, errs *fault.Bus,
) (string, graph.ContainerResolver, error) { ) (string, graph.ContainerResolver, error) {
var ( var (
cache = gcr restoreLoc = &path.Builder{}
restoreLoc = &path.Builder{} containerParentID string
containerParentID string defaultRootLocation = gcr.DefaultRootLocation()
) )
if len(defaultRootLocation) > 0 {
restoreLoc = restoreLoc.Append(defaultRootLocation)
}
for _, container := range destination.Elements() { for _, container := range destination.Elements() {
restoreLoc = restoreLoc.Append(container) restoreLoc = restoreLoc.Append(container)
@ -152,14 +156,14 @@ func CreateDestination(
containerID, err := getOrPopulateContainer( containerID, err := getOrPopulateContainer(
ictx, ictx,
ca, ca,
cache, gcr,
restoreLoc, restoreLoc,
resourceID, resourceID,
containerParentID, containerParentID,
container, container,
errs) errs)
if err != nil { if err != nil {
return "", cache, clues.Stack(err) return "", gcr, clues.Stack(err)
} }
containerParentID = containerID containerParentID = containerID
@ -167,7 +171,7 @@ func CreateDestination(
// containerParentID now identifies the last created container, // containerParentID now identifies the last created container,
// not its parent. // not its parent.
return containerParentID, cache, nil return containerParentID, gcr, nil
} }
func getOrPopulateContainer( func getOrPopulateContainer(

View File

@ -64,7 +64,7 @@ type ContainerResolver interface {
// @param ctx is necessary param for Graph API tracing // @param ctx is necessary param for Graph API tracing
// @param baseFolderID represents the M365ID base that the resolver will // @param baseFolderID represents the M365ID base that the resolver will
// conclude its search. Default input is "". // conclude its search. Default input is "".
Populate(ctx context.Context, errs *fault.Bus, baseFolderID string, baseContainerPath ...string) error Populate(ctx context.Context, errs *fault.Bus, baseFolderID string) error
// PathInCache performs a look up of a path representation // PathInCache performs a look up of a path representation
// and returns the m365ID of directory iff the pathString // and returns the m365ID of directory iff the pathString
@ -81,6 +81,11 @@ type ContainerResolver interface {
// Items returns the containers in the cache. // Items returns the containers in the cache.
Items() []CachedContainer Items() []CachedContainer
// DefaultRootLocation provides implementations which hard-code a parent
// folder root in their location path with a way to give downstream comparators
// that same location name.
DefaultRootLocation() string
} }
// ====================================== // ======================================