diff --git a/src/internal/kopia/wrapper_test.go b/src/internal/kopia/wrapper_test.go index 582c3ff78..9ad48cced 100644 --- a/src/internal/kopia/wrapper_test.go +++ b/src/internal/kopia/wrapper_test.go @@ -724,8 +724,10 @@ func TestKopiaIntegrationSuite(t *testing.T) { func (suite *KopiaIntegrationSuite) SetupSuite() { tmp, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, testInboxDir) @@ -736,8 +738,10 @@ func (suite *KopiaIntegrationSuite) SetupSuite() { tmp, err = path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, testArchiveDir) @@ -804,14 +808,14 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections() { reasons := []identity.Reasoner{ NewReason( testTenant, - suite.storePath1.ResourceOwner(), - suite.storePath1.Service(), + suite.storePath1.PrimaryProtectedResource(), + suite.storePath1.PrimaryService(), suite.storePath1.Category(), ), NewReason( testTenant, - suite.storePath2.ResourceOwner(), - suite.storePath2.Service(), + suite.storePath2.PrimaryProtectedResource(), + suite.storePath2.PrimaryService(), suite.storePath2.Category(), ), } @@ -1052,8 +1056,10 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections() { func (suite *KopiaIntegrationSuite) TestBackupCollections_NoDetailsForMeta() { tmp, err := path.Build( testTenant, - testUser, - path.OneDriveService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.OneDriveService, + }}, path.FilesCategory, false, testInboxDir) @@ -1079,8 +1085,8 @@ func (suite *KopiaIntegrationSuite) TestBackupCollections_NoDetailsForMeta() { reasons := []identity.Reasoner{ NewReason( testTenant, - storePath.ResourceOwner(), - storePath.Service(), + storePath.PrimaryProtectedResource(), + storePath.PrimaryService(), storePath.Category()), } @@ -1507,8 +1513,10 @@ func TestKopiaSimpleRepoIntegrationSuite(t *testing.T) { func (suite *KopiaSimpleRepoIntegrationSuite) SetupSuite() { tmp, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, testInboxDir) @@ -1518,8 +1526,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) SetupSuite() { tmp, err = path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, testArchiveDir) @@ -1800,8 +1810,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestBackupExcludeItem() { func (suite *KopiaSimpleRepoIntegrationSuite) TestProduceRestoreCollections() { doesntExist, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, true, "subdir", "foo") @@ -1934,8 +1946,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestProduceRestoreCollections() { func (suite *KopiaSimpleRepoIntegrationSuite) TestProduceRestoreCollections_PathChanges() { rp1, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, "corso_restore", "Inbox") @@ -1943,8 +1957,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestProduceRestoreCollections_Path rp2, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, "corso_restore", "Archive") @@ -2057,8 +2073,10 @@ func (suite *KopiaSimpleRepoIntegrationSuite) TestProduceRestoreCollections_Fetc rp1, err := path.Build( testTenant, - testUser, - path.ExchangeService, + []path.ServiceResource{{ + ProtectedResource: testUser, + Service: path.ExchangeService, + }}, path.EmailCategory, false, "corso_restore", "Inbox") diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 2dead366f..1a1046a92 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -538,8 +538,8 @@ func consumeBackupCollections( func matchesReason(reasons []identity.Reasoner, p path.Path) bool { for _, reason := range reasons { - if p.ResourceOwner() == reason.ProtectedResource() && - p.Service() == reason.Service() && + if p.PrimaryProtectedResource() == reason.ProtectedResource() && + p.PrimaryService() == reason.Service() && p.Category() == reason.Category() { return true } diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index 5538c4ce5..57613decd 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -617,13 +617,13 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsItems pathReason1 = kopia.NewReason( "", - itemPath1.ResourceOwner(), - itemPath1.Service(), + itemPath1.PrimaryProtectedResource(), + itemPath1.PrimaryService(), itemPath1.Category()) pathReason3 = kopia.NewReason( "", - itemPath3.ResourceOwner(), - itemPath3.Service(), + itemPath3.PrimaryProtectedResource(), + itemPath3.PrimaryService(), itemPath3.Category()) time1 = time.Now() @@ -1240,8 +1240,8 @@ func (suite *BackupOpUnitSuite) TestBackupOperation_MergeBackupDetails_AddsFolde pathReason1 = kopia.NewReason( "", - itemPath1.ResourceOwner(), - itemPath1.Service(), + itemPath1.PrimaryProtectedResource(), + itemPath1.PrimaryService(), itemPath1.Category()) backup1 = kopia.BackupEntry{ diff --git a/src/pkg/path/path.go b/src/pkg/path/path.go index 1cfa6b31d..1fbb9499f 100644 --- a/src/pkg/path/path.go +++ b/src/pkg/path/path.go @@ -78,47 +78,58 @@ var ( // Resources that don't have the requested information should return an empty // string. type Path interface { - String() string + // parts + + Tenant() string // ServiceResources produces all of the services and subservices, along with // the protected resource paired with the service, as contained in the path, // in their order of appearance. ServiceResources() []ServiceResource + // PrimaryService is the first service in ServiceResources() + PrimaryService() ServiceType + // PrimaryProtectedResource is the first ProtectedResource in ServiceResources() + PrimaryProtectedResource() string Category() CategoryType - Tenant() string Folder(escaped bool) string Folders() Elements Item() string - // UpdateParent updates parent from old to new if the item/folder was - // parented by old path - UpdateParent(prev, cur Path) bool - // PopFront returns a Builder object with the first element (left-side) - // removed. As the resulting set of elements is no longer a valid resource - // path a Builder is returned instead. - PopFront() *Builder - // Dir returns a Path object with the right-most element removed if possible. - // If removing the right-most element would discard one of the required prefix - // elements then an error is returned. - Dir() (Path, error) + + // type transformations + + // ToBuilder returns a Builder instance that represents the current Path. + ToBuilder() *Builder // Elements returns all the elements in the path. This is a temporary function // and will likely be updated to handle encoded elements instead of clear-text // elements in the future. Elements() Elements + // Halves breaks the path into its prefix (tenant, services, resources, category) + // and suffix (all parts after the prefix). If either half is empty, that half + // returns an empty, non-nil, value. + Halves() (*Builder, Elements) + // ShortRef returns a short reference representing this path. The short + // reference is guaranteed to be unique. No guarantees are made about whether + // a short reference can be converted back into the Path that generated it. + ShortRef() string + + // mutators + // Append returns a new Path object with the given element added to the end of // the old Path if possible. If the old Path is an item Path then Append // returns an error. Append(isItem bool, elems ...string) (Path, error) // AppendItem is a shorthand for Append(true, someItem) AppendItem(item string) (Path, error) - // ShortRef returns a short reference representing this path. The short - // reference is guaranteed to be unique. No guarantees are made about whether - // a short reference can be converted back into the Path that generated it. - ShortRef() string - // ToBuilder returns a Builder instance that represents the current Path. - ToBuilder() *Builder - // Halves breaks the path into its prefix (tenant, services, resources, category) - // and suffix (all parts after the prefix). If either half is empty, that half - // returns an empty, non-nil, value. - Halves() (*Builder, Elements) + // Dir returns a Path object with the right-most element removed if possible. + // If removing the right-most element would discard one of the required prefix + // elements then an error is returned. + Dir() (Path, error) + // PopFront returns a Builder object with the first element (left-side) + // removed. As the resulting set of elements is no longer a valid resource + // path a Builder is returned instead. + PopFront() *Builder + // UpdateParent updates parent from old to new if the item/folder was + // parented by old path + UpdateParent(prev, cur Path) bool // Every path needs to comply with these funcs to ensure that PII // is appropriately hidden from logging, errors, and other outputs. diff --git a/src/pkg/path/resource_path.go b/src/pkg/path/resource_path.go index 1bd380286..8093dde7e 100644 --- a/src/pkg/path/resource_path.go +++ b/src/pkg/path/resource_path.go @@ -51,6 +51,26 @@ func (rp dataLayerResourcePath) ServiceResources() []ServiceResource { return rp.serviceResources } +func (rp dataLayerResourcePath) PrimaryService() ServiceType { + srs := rp.serviceResources + + if len(srs) == 0 { + return UnknownService + } + + return srs[0].Service +} + +func (rp dataLayerResourcePath) PrimaryProtectedResource() string { + srs := rp.serviceResources + + if len(srs) == 0 { + return "" + } + + return srs[0].ProtectedResource +} + // Category returns the CategoryType embedded in the dataLayerResourcePath. func (rp dataLayerResourcePath) Category() CategoryType { return rp.category @@ -72,10 +92,16 @@ func (rp dataLayerResourcePath) lastFolderIdx() int { return endIdx } +func (rp dataLayerResourcePath) prefixLen() int { + return 2 + 2*len(rp.serviceResources) +} + // Folder returns the folder segment embedded in the dataLayerResourcePath. func (rp dataLayerResourcePath) Folder(escape bool) string { endIdx := rp.lastFolderIdx() - if endIdx == 4 { + pfxLen := rp.prefixLen() + + if endIdx == pfxLen { return "" } @@ -93,11 +119,14 @@ func (rp dataLayerResourcePath) Folder(escape bool) string { // dataLayerResourcePath. func (rp dataLayerResourcePath) Folders() Elements { endIdx := rp.lastFolderIdx() - if endIdx == 4 { + pfxLen := rp.prefixLen() + + // if endIdx == prefix length, there are no folders + if endIdx == pfxLen { return nil } - return append([]string{}, rp.elements[4:endIdx]...) + return append([]string{}, rp.elements[pfxLen:endIdx]...) } // Item returns the item embedded in the dataLayerResourcePath if the path diff --git a/src/pkg/path/resource_path_test.go b/src/pkg/path/resource_path_test.go index bc350612c..476124ffe 100644 --- a/src/pkg/path/resource_path_test.go +++ b/src/pkg/path/resource_path_test.go @@ -15,14 +15,34 @@ import ( ) const ( - testTenant = "aTenant" - testUser = "aUser" + testTenant = "aTenant" + testProtectedResource = "aProtectedResource" ) +func elemsWithWithoutItem(elems path.Elements) func(isItem bool) path.Elements { + return func(isItem bool) path.Elements { + if isItem { + return elems[:len(elems)-1] + } + + return elems + } +} + +func itemWithWithoutItem(elems path.Elements) func(isItem bool) string { + return func(isItem bool) string { + if isItem { + return elems[len(elems)-1] + } + + return "" + } +} + var ( // Purposely doesn't have characters that need escaping so it can be easily // computed using strings.Join(). - rest = []string{"some", "folder", "path", "with", "possible", "item"} + rest = path.Elements{"some", "folder", "path", "with", "possible", "item"} missingInfo = []struct { name string @@ -33,7 +53,7 @@ var ( { name: "NoTenant", tenant: "", - user: testUser, + user: testProtectedResource, rest: rest, }, { @@ -45,7 +65,7 @@ var ( { name: "NoFolderOrItem", tenant: testTenant, - user: testUser, + user: testProtectedResource, rest: nil, }, } @@ -70,60 +90,193 @@ var ( }, } - // Set of acceptable service/category mixtures. + // Set of acceptable service[/subservice]/category mixtures. serviceCategories = []struct { - service path.ServiceType - category path.CategoryType - pathFunc func(pb *path.Builder, tenant, user string, isItem bool) (path.Path, error) + name string + primaryService path.ServiceType + category path.CategoryType + pathFunc func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) + expectFolders func(expect path.Elements) func(isItem bool) path.Elements + expectItem func(expect path.Elements) func(isItem bool) string }{ { - service: path.ExchangeService, - category: path.EmailCategory, - pathFunc: func(pb *path.Builder, tenant, user string, isItem bool) (path.Path, error) { - return pb.ToDataLayerExchangePathForCategory(tenant, user, path.EmailCategory, isItem) + name: path.ExchangeService.String() + path.EmailCategory.String(), + primaryService: path.ExchangeService, + category: path.EmailCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.ExchangeService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.ExchangeService, - category: path.ContactsCategory, - pathFunc: func(pb *path.Builder, tenant, user string, isItem bool) (path.Path, error) { - return pb.ToDataLayerExchangePathForCategory(tenant, user, path.ContactsCategory, isItem) + name: path.ExchangeService.String() + path.ContactsCategory.String(), + primaryService: path.ExchangeService, + category: path.ContactsCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.ExchangeService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.ExchangeService, - category: path.EventsCategory, - pathFunc: func(pb *path.Builder, tenant, user string, isItem bool) (path.Path, error) { - return pb.ToDataLayerExchangePathForCategory(tenant, user, path.EventsCategory, isItem) + name: path.ExchangeService.String() + path.EventsCategory.String(), + primaryService: path.ExchangeService, + category: path.EventsCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.ExchangeService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.OneDriveService, - category: path.FilesCategory, - pathFunc: func(pb *path.Builder, tenant, user string, isItem bool) (path.Path, error) { - return pb.ToDataLayerOneDrivePath(tenant, user, isItem) + name: path.OneDriveService.String() + path.FilesCategory.String(), + primaryService: path.OneDriveService, + category: path.FilesCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.OneDriveService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.SharePointService, - category: path.LibrariesCategory, - pathFunc: func(pb *path.Builder, tenant, site string, isItem bool) (path.Path, error) { - return pb.ToDataLayerSharePointPath(tenant, site, path.LibrariesCategory, isItem) + name: path.SharePointService.String() + path.LibrariesCategory.String(), + primaryService: path.SharePointService, + category: path.LibrariesCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.SharePointService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.SharePointService, - category: path.ListsCategory, - pathFunc: func(pb *path.Builder, tenant, site string, isItem bool) (path.Path, error) { - return pb.ToDataLayerSharePointPath(tenant, site, path.ListsCategory, isItem) + name: path.SharePointService.String() + path.ListsCategory.String(), + primaryService: path.SharePointService, + category: path.ListsCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.SharePointService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, { - service: path.SharePointService, - category: path.PagesCategory, - pathFunc: func(pb *path.Builder, tenant, site string, isItem bool) (path.Path, error) { - return pb.ToDataLayerSharePointPath(tenant, site, path.PagesCategory, isItem) + name: path.SharePointService.String() + path.PagesCategory.String(), + primaryService: path.SharePointService, + category: path.PagesCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.SharePointService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, + }, + { + name: path.GroupsService.String() + path.UnknownCategory.String(), + primaryService: path.GroupsService, + category: path.UnknownCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources(path.GroupsService, primaryResource) + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) + }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, + }, + { + name: path.GroupsService.String() + path.SharePointService.String() + path.UnknownCategory.String(), + primaryService: path.GroupsService, + category: path.LibrariesCategory, + pathFunc: func( + tenant, primaryResource string, + isItem bool, + suffix path.Elements, + ) (path.Path, error) { + srs, err := path.NewServiceResources( + path.GroupsService, + primaryResource, + path.SharePointService, + "secondaryProtectedResource") + if err != nil { + return nil, err + } + + return path.Build(tenant, srs, path.PagesCategory, isItem, suffix...) + }, + expectFolders: elemsWithWithoutItem, + expectItem: itemWithWithoutItem, }, } ) @@ -142,22 +295,13 @@ func (suite *DataLayerResourcePath) SetupSuite() { func (suite *DataLayerResourcePath) TestMissingInfoErrors() { for _, types := range serviceCategories { - suite.Run(types.service.String()+types.category.String(), func() { + suite.Run(types.primaryService.String()+types.category.String(), func() { for _, m := range modes { suite.Run(m.name, func() { for _, test := range missingInfo { suite.Run(test.name, func() { - t := suite.T() - - b := path.Builder{}.Append(test.rest...) - - _, err := types.pathFunc( - b, - test.tenant, - test.user, - m.isItem, - ) - assert.Error(t, err) + _, err := types.pathFunc(test.tenant, test.user, m.isItem, rest) + assert.Error(suite.T(), err, clues.ToCore(err)) }) } }) @@ -167,31 +311,23 @@ func (suite *DataLayerResourcePath) TestMissingInfoErrors() { } func (suite *DataLayerResourcePath) TestMailItemNoFolder() { - item := "item" - b := path.Builder{}.Append(item) - - for _, types := range serviceCategories { - suite.Run(types.service.String()+types.category.String(), func() { + for _, test := range serviceCategories { + suite.Run(test.name, func() { t := suite.T() - p, err := types.pathFunc( - b, - testTenant, - testUser, - true, - ) + p, err := test.pathFunc(testTenant, testProtectedResource, true, path.Elements{"item"}) require.NoError(t, err, clues.ToCore(err)) assert.Empty(t, p.Folder(false)) assert.Empty(t, p.Folders()) - assert.Equal(t, item, p.Item()) + assert.Equal(t, test.expectItem(path.Elements{"item"})(true), p.Item()) }) } } func (suite *DataLayerResourcePath) TestPopFront() { expected := path.Builder{}.Append(append( - []string{path.ExchangeService.String(), testUser, path.EmailCategory.String()}, + []string{path.ExchangeService.String(), testProtectedResource, path.EmailCategory.String()}, rest..., )...) @@ -202,7 +338,7 @@ func (suite *DataLayerResourcePath) TestPopFront() { pb := path.Builder{}.Append(rest...) p, err := pb.ToDataLayerExchangePathForCategory( testTenant, - testUser, + testProtectedResource, path.EmailCategory, m.isItem, ) @@ -218,7 +354,7 @@ func (suite *DataLayerResourcePath) TestDir() { elements := []string{ testTenant, path.ExchangeService.String(), - testUser, + testProtectedResource, path.EmailCategory.String(), } @@ -227,7 +363,7 @@ func (suite *DataLayerResourcePath) TestDir() { pb := path.Builder{}.Append(rest...) p, err := pb.ToDataLayerExchangePathForCategory( testTenant, - testUser, + testProtectedResource, path.EmailCategory, m.isItem, ) @@ -391,7 +527,7 @@ func (suite *DataLayerResourcePath) TestToExchangePathForCategory() { p, err := b.ToDataLayerExchangePathForCategory( testTenant, - testUser, + testProtectedResource, test.category, m.isItem) test.check(t, err, clues.ToCore(err)) @@ -401,9 +537,9 @@ func (suite *DataLayerResourcePath) TestToExchangePathForCategory() { } assert.Equal(t, testTenant, p.Tenant()) - assert.Equal(t, path.ExchangeService, p.ServiceResources()[0].Service) + assert.Equal(t, path.ExchangeService, p.PrimaryService()) assert.Equal(t, test.category, p.Category()) - assert.Equal(t, testUser, p.ServiceResources()[0].ProtectedResource) + assert.Equal(t, testProtectedResource, p.PrimaryProtectedResource()) assert.Equal(t, strings.Join(m.expectedFolders, "/"), p.Folder(false)) assert.Equal(t, path.Elements(m.expectedFolders), p.Folders()) assert.Equal(t, m.expectedItem, p.Item()) @@ -416,7 +552,8 @@ func (suite *DataLayerResourcePath) TestToExchangePathForCategory() { type PopulatedDataLayerResourcePath struct { tester.Suite // Bool value is whether the path is an item path or a folder path. - paths map[bool]path.Path + serviceCategoriesToIsItemToPath map[string]map[bool]path.Path + isItemToPath map[bool]path.Path } func TestPopulatedDataLayerResourcePath(t *testing.T) { @@ -424,98 +561,109 @@ func TestPopulatedDataLayerResourcePath(t *testing.T) { } func (suite *PopulatedDataLayerResourcePath) SetupSuite() { - suite.paths = make(map[bool]path.Path, 2) - base := path.Builder{}.Append(rest...) + suite.serviceCategoriesToIsItemToPath = map[string]map[bool]path.Path{} - for _, t := range []bool{true, false} { - p, err := base.ToDataLayerExchangePathForCategory( - testTenant, - testUser, - path.EmailCategory, - t, - ) - require.NoError(suite.T(), err, clues.ToCore(err)) + for _, sc := range serviceCategories { + m := make(map[bool]path.Path, 2) + suite.serviceCategoriesToIsItemToPath[sc.name] = m - suite.paths[t] = p + for _, is := range []bool{true, false} { + p, err := sc.pathFunc(testTenant, testProtectedResource, is, rest) + require.NoError(suite.T(), err, clues.ToCore(err)) + + suite.serviceCategoriesToIsItemToPath[sc.name][is] = p + suite.isItemToPath[is] = p + } } } func (suite *PopulatedDataLayerResourcePath) TestTenant() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal(t, testTenant, suite.paths[m.isItem].Tenant()) + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), testTenant, p.Tenant()) + }) + } }) } } -func (suite *PopulatedDataLayerResourcePath) TestService() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal( - t, - path.ExchangeService, - suite.paths[m.isItem].ServiceResources()[0].Service) +func (suite *PopulatedDataLayerResourcePath) TestPrimaryService() { + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), test.primaryService, p.PrimaryService()) + }) + } }) } } func (suite *PopulatedDataLayerResourcePath) TestCategory() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal(t, path.EmailCategory, suite.paths[m.isItem].Category()) + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), test.category, p.Category()) + }) + } }) } } -func (suite *PopulatedDataLayerResourcePath) TestResourceOwner() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal( - t, - testUser, - suite.paths[m.isItem].ServiceResources()[0].ProtectedResource) +func (suite *PopulatedDataLayerResourcePath) TestPrimaryProtectedResource() { + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), testProtectedResource, p.PrimaryProtectedResource()) + }) + } }) } } func (suite *PopulatedDataLayerResourcePath) TestFolder() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal( - t, - strings.Join(m.expectedFolders, "/"), - suite.paths[m.isItem].Folder(false), - ) + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), test.expectFolders(rest)(m.isItem).String(), p.Folder(true)) + }) + } }) } } func (suite *PopulatedDataLayerResourcePath) TestFolders() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal(t, path.Elements(m.expectedFolders), suite.paths[m.isItem].Folders()) + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), test.expectFolders(rest)(m.isItem), p.Folders()) + }) + } }) } } func (suite *PopulatedDataLayerResourcePath) TestItem() { - for _, m := range modes { - suite.Run(m.name, func() { - t := suite.T() - - assert.Equal(t, m.expectedItem, suite.paths[m.isItem].Item()) + for _, test := range serviceCategories { + suite.Run(test.name, func() { + for _, m := range modes { + suite.Run(m.name, func() { + p := suite.serviceCategoriesToIsItemToPath[test.name][m.isItem] + assert.Equal(suite.T(), test.expectItem(rest)(m.isItem), p.Item()) + }) + } }) } } @@ -540,8 +688,7 @@ func (suite *PopulatedDataLayerResourcePath) TestAppend() { hasItem: false, expectedFolder: strings.Join( append(append([]string{}, rest...), newElement), - "/", - ), + "/"), expectedItem: "", }, } @@ -552,7 +699,7 @@ func (suite *PopulatedDataLayerResourcePath) TestAppend() { suite.Run(test.name, func() { t := suite.T() - newPath, err := suite.paths[m.isItem].Append(test.hasItem, newElement) + newPath, err := suite.isItemToPath[m.isItem].Append(test.hasItem, newElement) // Items don't allow appending. if m.isItem {