Populate location in Exchange container resolver (#2828)

Always generate both a path of IDs and a path of
display names for folders in the container resolver.

Changes in internal/connector/exchange/service_functions.go
keep the behavior of the overall system from changing

---

#### Does this PR need a docs update or release note?

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

* #2486

merge after:
* #2808

#### Test Plan

- [x] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2023-03-28 18:19:59 -07:00 committed by GitHub
parent e8245fb722
commit 91e0a27831
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 124 additions and 143 deletions

View File

@ -5,6 +5,7 @@ import (
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/path"
@ -31,7 +32,7 @@ func (cfc *contactFolderCache) populateContactRoot(
temp := graph.NewCacheFolder( temp := graph.NewCacheFolder(
f, f,
path.Builder{}.Append(baseContainerPath...), // storage path path.Builder{}.Append(ptr.Val(f.GetId())), // path of IDs
path.Builder{}.Append(baseContainerPath...)) // display location path.Builder{}.Append(baseContainerPath...)) // 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)
@ -59,7 +60,7 @@ func (cfc *contactFolderCache) Populate(
return clues.Wrap(err, "enumerating containers") return clues.Wrap(err, "enumerating containers")
} }
if err := cfc.populatePaths(ctx, false, errs); err != nil { if err := cfc.populatePaths(ctx, errs); err != nil {
return clues.Wrap(err, "populating paths") return clues.Wrap(err, "populating paths")
} }

View File

@ -53,16 +53,14 @@ type containerResolver struct {
func (cr *containerResolver) IDToPath( func (cr *containerResolver) IDToPath(
ctx context.Context, ctx context.Context,
folderID string, folderID string,
useIDInPath bool,
) (*path.Builder, *path.Builder, error) { ) (*path.Builder, *path.Builder, error) {
return cr.idToPath(ctx, folderID, 0, useIDInPath) return cr.idToPath(ctx, folderID, 0)
} }
func (cr *containerResolver) idToPath( func (cr *containerResolver) idToPath(
ctx context.Context, ctx context.Context,
folderID string, folderID string,
depth int, depth int,
useIDInPath bool,
) (*path.Builder, *path.Builder, error) { ) (*path.Builder, *path.Builder, error) {
ctx = clues.Add(ctx, "container_id", folderID) ctx = clues.Add(ctx, "container_id", folderID)
@ -83,33 +81,23 @@ func (cr *containerResolver) idToPath(
parentPath, parentLoc, err := cr.idToPath( parentPath, parentLoc, err := cr.idToPath(
ctx, ctx,
ptr.Val(c.GetParentFolderId()), ptr.Val(c.GetParentFolderId()),
depth+1, depth+1)
useIDInPath)
if err != nil { if err != nil {
return nil, nil, clues.Wrap(err, "retrieving parent folder") return nil, nil, clues.Wrap(err, "retrieving parent folder")
} }
toAppend := ptr.Val(c.GetDisplayName()) fullPath := parentPath.Append(ptr.Val(c.GetId()))
if useIDInPath {
toAppend = ptr.Val(c.GetId())
}
fullPath := parentPath.Append(toAppend)
c.SetPath(fullPath) c.SetPath(fullPath)
var locPath *path.Builder locPath := parentLoc.Append(ptr.Val(c.GetDisplayName()))
c.SetLocation(locPath)
if parentLoc != nil {
locPath = parentLoc.Append(ptr.Val(c.GetDisplayName()))
c.SetLocation(locPath)
}
return fullPath, locPath, nil return fullPath, locPath, nil
} }
// PathInCache utility function to return m365ID of folder if the path.Folders // PathInCache is a utility function to return m365ID of a folder if the
// matches the directory of a container within the cache. A boolean result // path.Folders matches the directory of a container within the cache. A boolean
// is provided to indicate whether the lookup was successful. // result is provided to indicate whether the lookup was successful.
func (cr *containerResolver) PathInCache(pathString string) (string, bool) { func (cr *containerResolver) PathInCache(pathString string) (string, bool) {
if len(pathString) == 0 || cr == nil { if len(pathString) == 0 || cr == nil {
return "", false return "", false
@ -128,6 +116,27 @@ func (cr *containerResolver) PathInCache(pathString string) (string, bool) {
return "", false return "", false
} }
// LocationInCache is a utility function to return m365ID of a folder if the
// path.Folders matches the directory of a container within the cache. A boolean
// result is provided to indicate whether the lookup was successful.
func (cr *containerResolver) LocationInCache(pathString string) (string, bool) {
if len(pathString) == 0 || cr == nil {
return "", false
}
for _, cc := range cr.cache {
if cc.Location() == nil {
continue
}
if cc.Location().String() == pathString {
return ptr.Val(cc.GetId()), true
}
}
return "", false
}
// addFolder adds a folder to the cache with the given ID. If the item is // addFolder adds a folder to the cache with the given ID. If the item is
// already in the cache does nothing. The path for the item is not modified. // already in the cache does nothing. The path for the item is not modified.
func (cr *containerResolver) addFolder(cf graph.CacheFolder) error { func (cr *containerResolver) addFolder(cf graph.CacheFolder) error {
@ -166,7 +175,6 @@ func (cr *containerResolver) Items() []graph.CachedContainer {
func (cr *containerResolver) AddToCache( func (cr *containerResolver) AddToCache(
ctx context.Context, ctx context.Context,
f graph.Container, f graph.Container,
useIDInPath bool,
) error { ) error {
temp := graph.CacheFolder{ temp := graph.CacheFolder{
Container: f, Container: f,
@ -177,7 +185,7 @@ func (cr *containerResolver) AddToCache(
// Populate the path for this entry so calls to PathInCache succeed no matter // Populate the path for this entry so calls to PathInCache succeed no matter
// when they're made. // when they're made.
_, _, err := cr.IDToPath(ctx, ptr.Val(f.GetId()), useIDInPath) _, _, err := cr.IDToPath(ctx, ptr.Val(f.GetId()))
if err != nil { if err != nil {
return clues.Wrap(err, "adding cache entry") return clues.Wrap(err, "adding cache entry")
} }
@ -185,15 +193,8 @@ func (cr *containerResolver) AddToCache(
return nil return nil
} }
// DestinationNameToID returns an empty string. This is only supported by exchange
// calendars at this time.
func (cr *containerResolver) DestinationNameToID(dest string) string {
return ""
}
func (cr *containerResolver) populatePaths( func (cr *containerResolver) populatePaths(
ctx context.Context, ctx context.Context,
useIDInPath bool,
errs *fault.Bus, errs *fault.Bus,
) error { ) error {
var ( var (
@ -207,7 +208,7 @@ func (cr *containerResolver) populatePaths(
return el.Failure() return el.Failure()
} }
_, _, err := cr.IDToPath(ctx, ptr.Val(f.GetId()), useIDInPath) _, _, err := cr.IDToPath(ctx, ptr.Val(f.GetId()))
if err != nil { if err != nil {
err = clues.Wrap(err, "populating path") err = clues.Wrap(err, "populating path")
el.AddRecoverable(err) el.AddRecoverable(err)

View File

@ -33,10 +33,10 @@ type mockContainer struct {
l *path.Builder l *path.Builder
} }
//nolint:revive //revive:disable-next-line:var-naming
func (m mockContainer) GetId() *string { return m.id } func (m mockContainer) GetId() *string { return m.id }
//nolint:revive //revive:disable-next-line:var-naming
func (m mockContainer) GetParentFolderId() *string { return m.parentID } func (m mockContainer) GetParentFolderId() *string { return m.parentID }
func (m mockContainer) GetDisplayName() *string { return m.displayName } func (m mockContainer) GetDisplayName() *string { return m.displayName }
func (m mockContainer) Location() *path.Builder { return m.l } func (m mockContainer) Location() *path.Builder { return m.l }
@ -277,11 +277,7 @@ func resolverWithContainers(numContainers int, useIDInPath bool) (*containerReso
// Base case for the recursive lookup. // Base case for the recursive lookup.
dn := containers[0].displayName dn := containers[0].displayName
apndP := containers[0].id
apndP := dn
if useIDInPath {
apndP = containers[0].id
}
containers[0].p = path.Builder{}.Append(apndP) containers[0].p = path.Builder{}.Append(apndP)
containers[0].expectedPath = apndP containers[0].expectedPath = apndP
@ -290,11 +286,7 @@ func resolverWithContainers(numContainers int, useIDInPath bool) (*containerReso
for i := 1; i < len(containers); i++ { for i := 1; i < len(containers); i++ {
dn := containers[i].displayName dn := containers[i].displayName
apndP := containers[i].id
apndP := dn
if useIDInPath {
apndP = containers[i].id
}
containers[i].parentID = containers[i-1].id containers[i].parentID = containers[i-1].id
containers[i].expectedPath = stdpath.Join(containers[i-1].expectedPath, apndP) containers[i].expectedPath = stdpath.Join(containers[i-1].expectedPath, apndP)
@ -358,7 +350,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestDepthLimit() {
for _, test := range table { for _, test := range table {
suite.Run(test.name, func() { suite.Run(test.name, func() {
resolver, containers := resolverWithContainers(test.numContainers, false) resolver, containers := resolverWithContainers(test.numContainers, false)
_, _, err := resolver.IDToPath(ctx, containers[len(containers)-1].id, false) _, _, err := resolver.IDToPath(ctx, containers[len(containers)-1].id)
test.check(suite.T(), err, clues.ToCore(err)) test.check(suite.T(), err, clues.ToCore(err))
}) })
} }
@ -370,7 +362,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestPopulatePaths() {
t := suite.T() t := suite.T()
err := suite.fc.populatePaths(ctx, false, fault.New(true)) err := suite.fc.populatePaths(ctx, fault.New(true))
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
items := suite.fc.Items() items := suite.fc.Items()
@ -396,7 +388,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderNoPathsCached
suite.Run(ptr.Val(c.GetDisplayName()), func() { suite.Run(ptr.Val(c.GetDisplayName()), func() {
t := suite.T() t := suite.T()
p, l, err := suite.fc.IDToPath(ctx, c.id, false) p, l, err := suite.fc.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
@ -412,7 +404,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderNoPathsCached
suite.Run(ptr.Val(c.GetDisplayName()), func() { suite.Run(ptr.Val(c.GetDisplayName()), func() {
t := suite.T() t := suite.T()
p, l, err := suite.fcWithID.IDToPath(ctx, c.id, true) p, l, err := suite.fcWithID.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
@ -427,14 +419,14 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderCachesPaths()
t := suite.T() t := suite.T()
c := suite.allContainers[len(suite.allContainers)-1] c := suite.allContainers[len(suite.allContainers)-1]
p, l, err := suite.fc.IDToPath(ctx, c.id, false) p, l, err := suite.fc.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
c.parentID = "foo" c.parentID = "foo"
p, l, err = suite.fc.IDToPath(ctx, c.id, false) p, l, err = suite.fc.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
@ -447,14 +439,14 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderCachesPaths_u
t := suite.T() t := suite.T()
c := suite.containersWithID[len(suite.containersWithID)-1] c := suite.containersWithID[len(suite.containersWithID)-1]
p, l, err := suite.fcWithID.IDToPath(ctx, c.id, true) p, l, err := suite.fcWithID.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
c.parentID = "foo" c.parentID = "foo"
p, l, err = suite.fcWithID.IDToPath(ctx, c.id, true) p, l, err = suite.fcWithID.IDToPath(ctx, c.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, c.expectedPath, p.String()) assert.Equal(t, c.expectedPath, p.String())
assert.Equal(t, c.expectedLocation, l.String()) assert.Equal(t, c.expectedLocation, l.String())
@ -470,7 +462,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderErrorsParentN
delete(suite.fc.cache, almostLast.id) delete(suite.fc.cache, almostLast.id)
_, _, err := suite.fc.IDToPath(ctx, last.id, false) _, _, err := suite.fc.IDToPath(ctx, last.id)
assert.Error(t, err, clues.ToCore(err)) assert.Error(t, err, clues.ToCore(err))
} }
@ -480,7 +472,7 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestLookupCachedFolderErrorsNotFoun
t := suite.T() t := suite.T()
_, _, err := suite.fc.IDToPath(ctx, "foo", false) _, _, err := suite.fc.IDToPath(ctx, "foo")
assert.Error(t, err, clues.ToCore(err)) assert.Error(t, err, clues.ToCore(err))
} }
@ -496,20 +488,16 @@ func (suite *ConfiguredFolderCacheUnitSuite) TestAddToCache() {
) )
m.parentID = last.id m.parentID = last.id
m.expectedPath = stdpath.Join(last.expectedPath, m.displayName) m.expectedPath = stdpath.Join(last.expectedPath, m.id)
m.expectedLocation = stdpath.Join(last.expectedPath, m.displayName) m.expectedLocation = stdpath.Join(last.expectedLocation, m.displayName)
require.Empty(t, suite.fc.DestinationNameToID(dest), "destination not yet added to cache") err := suite.fc.AddToCache(ctx, m)
err := suite.fc.AddToCache(ctx, m, false)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
require.Empty(t, suite.fc.DestinationNameToID(dest),
"destination id from cache, still empty, because this is not a calendar")
p, l, err := suite.fc.IDToPath(ctx, m.id, false) p, l, err := suite.fc.IDToPath(ctx, m.id)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
assert.Equal(t, m.expectedPath, p.String()) assert.Equal(t, m.expectedPath, p.String(), "ID path")
assert.Equal(t, m.expectedLocation, l.String()) assert.Equal(t, m.expectedLocation, l.String(), "location path")
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -568,7 +556,6 @@ func (suite *FolderCacheIntegrationSuite) TestCreateContainerDestination() {
pathFunc2 func(t *testing.T) path.Path pathFunc2 func(t *testing.T) path.Path
category path.CategoryType category path.CategoryType
folderPrefix string folderPrefix string
useIDForPath bool
}{ }{
{ {
name: "Mail Cache Test", name: "Mail Cache Test",
@ -627,9 +614,8 @@ func (suite *FolderCacheIntegrationSuite) TestCreateContainerDestination() {
}, },
}, },
{ {
name: "Event Cache Test", name: "Event Cache Test",
category: path.EventsCategory, category: path.EventsCategory,
useIDForPath: true,
pathFunc1: func(t *testing.T) path.Path { pathFunc1: func(t *testing.T) path.Path {
pth, err := path.Build( pth, err := path.Build(
suite.credentials.AzureTenantID, suite.credentials.AzureTenantID,
@ -673,28 +659,25 @@ func (suite *FolderCacheIntegrationSuite) TestCreateContainerDestination() {
resolver := directoryCaches[test.category] resolver := directoryCaches[test.category]
_, _, err = resolver.IDToPath(ctx, folderID, test.useIDForPath) _, _, err = resolver.IDToPath(ctx, folderID)
assert.NoError(t, err, clues.ToCore(err)) assert.NoError(t, err, clues.ToCore(err))
parentContainer := folderName
if test.useIDForPath {
parentContainer = folderID
}
secondID, err := CreateContainerDestination( secondID, err := CreateContainerDestination(
ctx, ctx,
m365, m365,
test.pathFunc2(t), test.pathFunc2(t),
parentContainer, folderName,
directoryCaches, directoryCaches,
fault.New(true)) fault.New(true))
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
_, _, err = resolver.IDToPath(ctx, secondID, test.useIDForPath) p, l, err := resolver.IDToPath(ctx, secondID)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
p := stdpath.Join(test.folderPrefix, parentContainer) _, ok := resolver.LocationInCache(l.String())
_, ok := resolver.PathInCache(p) require.True(t, ok, "looking for location in cache: %s", l)
_, ok = resolver.PathInCache(p.String())
require.True(t, ok, "looking for path in cache: %s", p) require.True(t, ok, "looking for path in cache: %s", p)
}) })
} }

View File

@ -15,10 +15,9 @@ var _ graph.ContainerResolver = &eventCalendarCache{}
type eventCalendarCache struct { type eventCalendarCache struct {
*containerResolver *containerResolver
enumer containersEnumerator enumer containersEnumerator
getter containerGetter getter containerGetter
userID string userID string
newAdditions map[string]string
} }
// init ensures that the structure's fields are initialized. // init ensures that the structure's fields are initialized.
@ -80,7 +79,7 @@ func (ecc *eventCalendarCache) Populate(
return clues.Wrap(err, "enumerating containers") return clues.Wrap(err, "enumerating containers")
} }
if err := ecc.populatePaths(ctx, true, errs); err != nil { if err := ecc.populatePaths(ctx, errs); err != nil {
return clues.Wrap(err, "establishing calendar paths") return clues.Wrap(err, "establishing calendar paths")
} }
@ -89,7 +88,7 @@ func (ecc *eventCalendarCache) Populate(
// AddToCache adds container to map in field 'cache' // AddToCache adds container to map in field 'cache'
// @returns error iff the required values are not accessible. // @returns error iff the required values are not accessible.
func (ecc *eventCalendarCache) AddToCache(ctx context.Context, f graph.Container, useIDInPath bool) error { func (ecc *eventCalendarCache) AddToCache(ctx context.Context, f graph.Container) error {
if err := checkIDAndName(f); err != nil { if err := checkIDAndName(f); err != nil {
return clues.Wrap(err, "validating container").WithClues(ctx) return clues.Wrap(err, "validating container").WithClues(ctx)
} }
@ -99,30 +98,16 @@ func (ecc *eventCalendarCache) AddToCache(ctx context.Context, f graph.Container
path.Builder{}.Append(ptr.Val(f.GetId())), // storage path path.Builder{}.Append(ptr.Val(f.GetId())), // storage path
path.Builder{}.Append(ptr.Val(f.GetDisplayName()))) // display location path.Builder{}.Append(ptr.Val(f.GetDisplayName()))) // display location
if len(ecc.newAdditions) == 0 {
ecc.newAdditions = map[string]string{}
}
ecc.newAdditions[ptr.Val(f.GetDisplayName())] = ptr.Val(f.GetId())
if err := ecc.addFolder(temp); err != nil { if err := ecc.addFolder(temp); err != nil {
delete(ecc.newAdditions, ptr.Val(f.GetDisplayName()))
return clues.Wrap(err, "adding container").WithClues(ctx) return clues.Wrap(err, "adding container").WithClues(ctx)
} }
// Populate the path for this entry so calls to PathInCache succeed no matter // Populate the path for this entry so calls to PathInCache succeed no matter
// when they're made. // when they're made.
_, _, err := ecc.IDToPath(ctx, ptr.Val(f.GetId()), true) _, _, err := ecc.IDToPath(ctx, ptr.Val(f.GetId()))
if err != nil { if err != nil {
delete(ecc.newAdditions, ptr.Val(f.GetDisplayName()))
return clues.Wrap(err, "setting path to container id") return clues.Wrap(err, "setting path to container id")
} }
return nil return nil
} }
// DestinationNameToID returns an empty string. This is only supported by exchange
// calendars at this time.
func (ecc *eventCalendarCache) DestinationNameToID(dest string) string {
return ecc.newAdditions[dest]
}

View File

@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/connector/exchange/api" "github.com/alcionai/corso/src/internal/connector/exchange/api"
"github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph"
"github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/internal/tester"
@ -47,9 +46,6 @@ func (suite *CacheResolverSuite) TestPopulate() {
ac, err := api.NewClient(suite.credentials) ac, err := api.NewClient(suite.credentials)
require.NoError(suite.T(), err, clues.ToCore(err)) require.NoError(suite.T(), err, clues.ToCore(err))
cal, err := ac.Events().GetContainerByID(ctx, tester.M365UserID(suite.T()), DefaultCalendar)
require.NoError(suite.T(), err, clues.ToCore(err))
eventFunc := func(t *testing.T) graph.ContainerResolver { eventFunc := func(t *testing.T) graph.ContainerResolver {
return &eventCalendarCache{ return &eventCalendarCache{
userID: tester.M365UserID(t), userID: tester.M365UserID(t),
@ -72,8 +68,9 @@ func (suite *CacheResolverSuite) TestPopulate() {
canFind assert.BoolAssertionFunc canFind assert.BoolAssertionFunc
}{ }{
{ {
name: "Default Event Cache", name: "Default Event Cache",
folderInCache: ptr.Val(cal.GetId()), // Fine as long as this isn't running against a migrated Exchange server.
folderInCache: DefaultCalendar,
root: DefaultCalendar, root: DefaultCalendar,
basePath: DefaultCalendar, basePath: DefaultCalendar,
resolverFunc: eventFunc, resolverFunc: eventFunc,
@ -124,8 +121,8 @@ func (suite *CacheResolverSuite) TestPopulate() {
err := resolver.Populate(ctx, fault.New(true), test.root, test.basePath) err := resolver.Populate(ctx, fault.New(true), test.root, test.basePath)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
_, isFound := resolver.PathInCache(test.folderInCache) _, isFound := resolver.LocationInCache(test.folderInCache)
test.canFind(t, isFound) test.canFind(t, isFound, "folder path", test.folderInCache)
}) })
} }
} }

View File

@ -50,7 +50,7 @@ func (mc *mailFolderCache) populateMailRoot(ctx context.Context) error {
// Root folder doesn't store any mail messages so it isn't given any paths. // Root folder doesn't store any mail messages so it isn't given any paths.
// Giving it a path/location would cause us to add extra path elements that // Giving it a path/location would cause us to add extra path elements that
// the user doesn't see in the regular UI for Exchange. // the user doesn't see in the regular UI for Exchange.
path.Builder{}.Append(), // storage path path.Builder{}.Append(), // path of IDs
path.Builder{}.Append()) // display location path.Builder{}.Append()) // display location
if err := mc.addFolder(temp); err != nil { if err := mc.addFolder(temp); err != nil {
return clues.Wrap(err, "adding resolver dir").WithClues(ctx) return clues.Wrap(err, "adding resolver dir").WithClues(ctx)
@ -79,7 +79,7 @@ func (mc *mailFolderCache) Populate(
return clues.Wrap(err, "enumerating containers") return clues.Wrap(err, "enumerating containers")
} }
if err := mc.populatePaths(ctx, false, errs); err != nil { if err := mc.populatePaths(ctx, errs); err != nil {
return clues.Wrap(err, "populating paths") return clues.Wrap(err, "populating paths")
} }

View File

@ -96,14 +96,14 @@ func (suite *MailFolderCacheIntegrationSuite) TestDeltaFetch() {
err = mfc.Populate(ctx, fault.New(true), test.root, test.path...) err = mfc.Populate(ctx, fault.New(true), test.root, test.path...)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
p, l, err := mfc.IDToPath(ctx, testFolderID, true) p, l, err := mfc.IDToPath(ctx, testFolderID)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
t.Logf("Path: %s\n", p.String()) t.Logf("Path: %s\n", p.String())
t.Logf("Location: %s\n", l.String()) t.Logf("Location: %s\n", l.String())
expectedPath := stdpath.Join(append(test.path, expectedFolderPath)...) expectedPath := stdpath.Join(append(test.path, expectedFolderPath)...)
assert.Equal(t, expectedPath, p.String()) assert.Equal(t, expectedPath, p.String())
identifier, ok := mfc.PathInCache(p.String()) identifier, ok := mfc.LocationInCache(p.String())
assert.True(t, ok) assert.True(t, ok)
assert.NotEmpty(t, identifier) assert.NotEmpty(t, identifier)
}) })

View File

@ -106,11 +106,7 @@ func includeContainer(
// Clause ensures that DefaultContactFolder is inspected properly // Clause ensures that DefaultContactFolder is inspected properly
if category == path.ContactsCategory && ptr.Val(c.GetDisplayName()) == DefaultContactFolder { if category == path.ContactsCategory && ptr.Val(c.GetDisplayName()) == DefaultContactFolder {
pb = pb.Append(DefaultContactFolder) loc = loc.Append(DefaultContactFolder)
if loc != nil {
loc = loc.Append(DefaultContactFolder)
}
} }
dirPath, err := pb.ToDataLayerExchangePathForCategory( dirPath, err := pb.ToDataLayerExchangePathForCategory(
@ -139,18 +135,24 @@ func includeContainer(
directory = locPath.Folder(false) directory = locPath.Folder(false)
} }
var ok bool var (
ok bool
pathRes path.Path
)
switch category { switch category {
case path.EmailCategory: case path.EmailCategory:
ok = scope.Matches(selectors.ExchangeMailFolder, directory) ok = scope.Matches(selectors.ExchangeMailFolder, directory)
pathRes = locPath
case path.ContactsCategory: case path.ContactsCategory:
ok = scope.Matches(selectors.ExchangeContactFolder, directory) ok = scope.Matches(selectors.ExchangeContactFolder, directory)
pathRes = locPath
case path.EventsCategory: case path.EventsCategory:
ok = scope.Matches(selectors.ExchangeEventCalendar, directory) ok = scope.Matches(selectors.ExchangeEventCalendar, directory)
pathRes = dirPath
default: default:
return nil, nil, false return nil, nil, false
} }
return dirPath, locPath, ok return pathRes, locPath, ok
} }

View File

@ -78,7 +78,7 @@ func (m mockResolver) Items() []graph.CachedContainer {
return m.items return m.items
} }
func (m mockResolver) AddToCache(ctx context.Context, gc graph.Container, b bool) error { func (m mockResolver) AddToCache(ctx context.Context, gc graph.Container) error {
if len(m.added) == 0 { if len(m.added) == 0 {
m.added = map[string]string{} m.added = map[string]string{}
} }
@ -88,10 +88,11 @@ func (m mockResolver) AddToCache(ctx context.Context, gc graph.Container, b bool
return nil return nil
} }
func (m mockResolver) DestinationNameToID(dest string) string { return m.added[dest] } func (m mockResolver) DestinationNameToID(dest string) string { return m.added[dest] }
func (m mockResolver) IDToPath(context.Context, string, bool) (*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) Populate(context.Context, *fault.Bus, string, ...string) error { return nil } func (m mockResolver) Populate(context.Context, *fault.Bus, string, ...string) error { return nil }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -145,12 +146,14 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections() {
container1 = mockContainer{ container1 = mockContainer{
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("display_name_1"), displayName: strPtr("display_name_1"),
p: path.Builder{}.Append("display_name_1"), p: path.Builder{}.Append("1"),
l: path.Builder{}.Append("display_name_1"),
} }
container2 = mockContainer{ container2 = mockContainer{
id: strPtr("2"), id: strPtr("2"),
displayName: strPtr("display_name_2"), displayName: strPtr("display_name_2"),
p: path.Builder{}.Append("display_name_2"), p: path.Builder{}.Append("2"),
l: path.Builder{}.Append("display_name_2"),
} }
) )
@ -445,7 +448,8 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_repea
container1 = mockContainer{ container1 = mockContainer{
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("display_name_1"), displayName: strPtr("display_name_1"),
p: path.Builder{}.Append("display_name_1"), p: path.Builder{}.Append("1"),
l: path.Builder{}.Append("display_name_1"),
} }
resolver = newMockResolver(container1) resolver = newMockResolver(container1)
) )
@ -562,6 +566,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("new"), displayName: strPtr("new"),
p: path.Builder{}.Append("1", "new"), p: path.Builder{}.Append("1", "new"),
l: path.Builder{}.Append("1", "new"),
}), }),
dps: DeltaPaths{}, dps: DeltaPaths{},
expect: map[string]endState{ expect: map[string]endState{
@ -577,6 +582,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("not_moved"), displayName: strPtr("not_moved"),
p: path.Builder{}.Append("1", "not_moved"), p: path.Builder{}.Append("1", "not_moved"),
l: path.Builder{}.Append("1", "not_moved"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -597,6 +603,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("moved"), displayName: strPtr("moved"),
p: path.Builder{}.Append("1", "moved"), p: path.Builder{}.Append("1", "moved"),
l: path.Builder{}.Append("1", "moved"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -631,6 +638,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("2"), id: strPtr("2"),
displayName: strPtr("new"), displayName: strPtr("new"),
p: path.Builder{}.Append("2", "new"), p: path.Builder{}.Append("2", "new"),
l: path.Builder{}.Append("2", "new"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -652,6 +660,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("2"), id: strPtr("2"),
displayName: strPtr("same"), displayName: strPtr("same"),
p: path.Builder{}.Append("2", "same"), p: path.Builder{}.Append("2", "same"),
l: path.Builder{}.Append("2", "same"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -675,11 +684,13 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("moved"), displayName: strPtr("moved"),
p: path.Builder{}.Append("1", "moved"), p: path.Builder{}.Append("1", "moved"),
l: path.Builder{}.Append("1", "moved"),
}, },
mockContainer{ mockContainer{
id: strPtr("2"), id: strPtr("2"),
displayName: strPtr("prev"), displayName: strPtr("prev"),
p: path.Builder{}.Append("2", "prev"), p: path.Builder{}.Append("2", "prev"),
l: path.Builder{}.Append("2", "prev"),
}, },
), ),
dps: DeltaPaths{ dps: DeltaPaths{
@ -702,6 +713,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("not_moved"), displayName: strPtr("not_moved"),
p: path.Builder{}.Append("1", "not_moved"), p: path.Builder{}.Append("1", "not_moved"),
l: path.Builder{}.Append("1", "not_moved"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -726,6 +738,7 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("same"), displayName: strPtr("same"),
p: path.Builder{}.Append("1", "same"), p: path.Builder{}.Append("1", "same"),
l: path.Builder{}.Append("1", "same"),
}), }),
dps: DeltaPaths{ dps: DeltaPaths{
"1": DeltaPath{ "1": DeltaPath{
@ -751,21 +764,25 @@ func (suite *ServiceIteratorsSuite) TestFilterContainersAndFillCollections_incre
id: strPtr("1"), id: strPtr("1"),
displayName: strPtr("new"), displayName: strPtr("new"),
p: path.Builder{}.Append("1", "new"), p: path.Builder{}.Append("1", "new"),
l: path.Builder{}.Append("1", "new"),
}, },
mockContainer{ mockContainer{
id: strPtr("2"), id: strPtr("2"),
displayName: strPtr("not_moved"), displayName: strPtr("not_moved"),
p: path.Builder{}.Append("2", "not_moved"), p: path.Builder{}.Append("2", "not_moved"),
l: path.Builder{}.Append("2", "not_moved"),
}, },
mockContainer{ mockContainer{
id: strPtr("3"), id: strPtr("3"),
displayName: strPtr("moved"), displayName: strPtr("moved"),
p: path.Builder{}.Append("3", "moved"), p: path.Builder{}.Append("3", "moved"),
l: path.Builder{}.Append("3", "moved"),
}, },
mockContainer{ mockContainer{
id: strPtr("4"), id: strPtr("4"),
displayName: strPtr("moved"), displayName: strPtr("moved"),
p: path.Builder{}.Append("4", "moved"), p: path.Builder{}.Append("4", "moved"),
l: path.Builder{}.Append("4", "moved"),
}, },
), ),
dps: DeltaPaths{ dps: DeltaPaths{

View File

@ -555,11 +555,6 @@ func CreateContainerDestination(
caches[category] = ecc caches[category] = ecc
newCache = true newCache = true
directoryCache = ecc directoryCache = ecc
} else if did := directoryCache.DestinationNameToID(dest); len(did) > 0 {
// calendars are cached by ID in the resolver, not name, so once we have
// created the destination calendar, we need to look up its id and use
// that for resolver lookups instead of the display name.
dest = did
} }
folders := append([]string{dest}, directory.Folders()...) folders := append([]string{dest}, directory.Folders()...)
@ -602,7 +597,7 @@ func establishMailRestoreLocation(
for _, folder := range folders { for _, folder := range folders {
pb = *pb.Append(folder) pb = *pb.Append(folder)
cached, ok := mfc.PathInCache(pb.String()) cached, ok := mfc.LocationInCache(pb.String())
if ok { if ok {
folderID = cached folderID = cached
continue continue
@ -628,7 +623,7 @@ func establishMailRestoreLocation(
} }
// NOOP if the folder is already in the cache. // NOOP if the folder is already in the cache.
if err = mfc.AddToCache(ctx, temp, false); err != nil { if err = mfc.AddToCache(ctx, temp); err != nil {
return "", clues.Wrap(err, "adding folder to cache") return "", clues.Wrap(err, "adding folder to cache")
} }
} }
@ -651,7 +646,7 @@ func establishContactsRestoreLocation(
isNewCache bool, isNewCache bool,
errs *fault.Bus, errs *fault.Bus,
) (string, error) { ) (string, error) {
cached, ok := cfc.PathInCache(folders[0]) cached, ok := cfc.LocationInCache(folders[0])
if ok { if ok {
return cached, nil return cached, nil
} }
@ -670,7 +665,7 @@ func establishContactsRestoreLocation(
return "", clues.Wrap(err, "populating contact cache") return "", clues.Wrap(err, "populating contact cache")
} }
if err = cfc.AddToCache(ctx, temp, false); err != nil { if err = cfc.AddToCache(ctx, temp); err != nil {
return "", clues.Wrap(err, "adding contact folder to cache") return "", clues.Wrap(err, "adding contact folder to cache")
} }
} }
@ -688,7 +683,7 @@ func establishEventsRestoreLocation(
errs *fault.Bus, errs *fault.Bus,
) (string, error) { ) (string, error) {
// Need to prefix with the "Other Calendars" folder so lookup happens properly. // Need to prefix with the "Other Calendars" folder so lookup happens properly.
cached, ok := ecc.PathInCache(folders[0]) cached, ok := ecc.LocationInCache(folders[0])
if ok { if ok {
return cached, nil return cached, nil
} }
@ -708,7 +703,7 @@ func establishEventsRestoreLocation(
} }
displayable := api.CalendarDisplayable{Calendarable: temp} displayable := api.CalendarDisplayable{Calendarable: temp}
if err = ecc.AddToCache(ctx, displayable, true); err != nil { if err = ecc.AddToCache(ctx, displayable); err != nil {
return "", clues.Wrap(err, "adding new calendar to cache") return "", clues.Wrap(err, "adding new calendar to cache")
} }
} }

View File

@ -58,7 +58,7 @@ type ContainerResolver interface {
// IDToPath takes an m365 container ID and converts it to a hierarchical path // IDToPath takes an m365 container ID and converts it to a hierarchical path
// to that container. The path has a similar format to paths on the local // to that container. The path has a similar format to paths on the local
// file system. // file system.
IDToPath(ctx context.Context, m365ID string, useIDInPath bool) (*path.Builder, *path.Builder, error) IDToPath(ctx context.Context, m365ID string) (*path.Builder, *path.Builder, error)
// Populate performs initialization steps for the resolver // Populate performs initialization steps for the resolver
// @param ctx is necessary param for Graph API tracing // @param ctx is necessary param for Graph API tracing
@ -71,13 +71,13 @@ type ContainerResolver interface {
// matches the path of a container within the cache. // matches the path of a container within the cache.
// @returns bool represents if m365ID was found. // @returns bool represents if m365ID was found.
PathInCache(pathString string) (string, bool) PathInCache(pathString string) (string, bool)
// LocationInCache performs a look up of a path reprensentation
// and returns the m365ID of directory iff the pathString
// matches the logical path of a container within the cache.
// @returns bool represents if m365ID was found.
LocationInCache(pathString string) (string, bool)
AddToCache(ctx context.Context, m365Container Container, useIDInPath bool) error AddToCache(ctx context.Context, m365Container Container) error
// DestinationNameToID returns the ID of the destination container. Dest is
// assumed to be a display name. The ID is only populated if the destination
// was added using `AddToCache()`. Returns an empty string if not found.
DestinationNameToID(dest string) string
// Items returns the containers in the cache. // Items returns the containers in the cache.
Items() []CachedContainer Items() []CachedContainer

View File

@ -784,7 +784,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() {
p, err := path.FromDataLayerPath(dest.deets.Entries[0].RepoRef, true) p, err := path.FromDataLayerPath(dest.deets.Entries[0].RepoRef, true)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
id, ok := cr.PathInCache(p.Folder(false)) id, ok := cr.LocationInCache(p.Folder(false))
require.True(t, ok, "dir %s found in %s cache", p.Folder(false), category) require.True(t, ok, "dir %s found in %s cache", p.Folder(false), category)
d := dataset[category].dests[destName] d := dataset[category].dests[destName]
@ -897,7 +897,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() {
p, err := path.FromDataLayerPath(deets.Entries[0].RepoRef, true) p, err := path.FromDataLayerPath(deets.Entries[0].RepoRef, true)
require.NoError(t, err, clues.ToCore(err)) require.NoError(t, err, clues.ToCore(err))
id, ok := cr.PathInCache(p.Folder(false)) id, ok := cr.LocationInCache(p.Folder(false))
require.Truef(t, ok, "dir %s found in %s cache", p.Folder(false), category) require.Truef(t, ok, "dir %s found in %s cache", p.Folder(false), category)
dataset[category].dests[container3] = contDeets{id, deets} dataset[category].dests[container3] = contDeets{id, deets}