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.
This commit is contained in:
parent
8d2070409f
commit
135c638324
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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 ""
|
||||||
|
}
|
||||||
|
|||||||
@ -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 ""
|
||||||
|
}
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ======================================
|
// ======================================
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user