Create path categories for metadata folders (#1718)

## Description

Corso needs to store some transient information between backups so that it can perform more efficient incremental backups. This transient information will live at a path `tenant-id/service/user-id/categoryMetadata`. This patch adds an initializer, category enums, and tests for these new path locations

## Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Test
- [ ] 💻 CI/Deployment
- [ ] 🐹 Trivial/Minor

## Issue(s)

* #1685

## Test Plan

<!-- How will this be tested prior to merging.-->
- [ ] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
This commit is contained in:
ashmrtn 2022-12-06 18:32:49 -08:00 committed by GitHub
parent 24f60af404
commit 5c7d2168fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 180 additions and 8 deletions

View File

@ -250,7 +250,7 @@ func (pb Builder) join(start, end int) string {
return join(pb.elements[start:end])
}
func (pb Builder) verifyPrefix(tenant, resourceOwner string) error {
func verifyInputValues(tenant, resourceOwner string) error {
if len(tenant) == 0 {
return errors.Wrap(errMissingSegment, "tenant")
}
@ -259,6 +259,14 @@ func (pb Builder) verifyPrefix(tenant, resourceOwner string) error {
return errors.Wrap(errMissingSegment, "resourceOwner")
}
return nil
}
func (pb Builder) verifyPrefix(tenant, resourceOwner string) error {
if err := verifyInputValues(tenant, resourceOwner); err != nil {
return err
}
if len(pb.elements) == 0 {
return errors.New("missing path beyond prefix")
}
@ -273,6 +281,48 @@ func (pb Builder) withPrefix(elements ...string) *Builder {
return res
}
func (pb Builder) ToServiceCategoryMetadataPath(
tenant, user string,
service ServiceType,
category CategoryType,
isItem bool,
) (Path, error) {
if err := validateServiceAndCategory(service, category); err != nil {
return nil, err
}
if err := verifyInputValues(tenant, user); err != nil {
return nil, err
}
if isItem && len(pb.elements) == 0 {
return nil, errors.New("missing path beyond prefix")
}
metadataService := UnknownService
switch service {
case ExchangeService:
metadataService = ExchangeMetadataService
case OneDriveService:
metadataService = OneDriveMetadataService
case SharePointService:
metadataService = SharePointMetadataService
}
return &dataLayerResourcePath{
Builder: *pb.withPrefix(
tenant,
metadataService.String(),
user,
category.String(),
),
service: metadataService,
category: category,
hasItem: isItem,
}, nil
}
func (pb Builder) ToDataLayerExchangePathForCategory(
tenant, user string,
category CategoryType,

View File

@ -6,6 +6,16 @@ import (
var ErrorUnknownService = errors.New("unknown service string")
// ServiceType denotes what service the path corresponds to. Metadata services
// are also included though they are only used for paths that house metadata for
// Corso backups.
//
// Metadata services are not considered valid service types for resource paths
// though they can be used for metadata paths.
//
// The order of the enums below can be changed, but the string representation of
// each enum must remain the same or migration code needs to be added to handle
// changes to the string format.
type ServiceType int
//go:generate stringer -type=ServiceType -linecomment
@ -14,6 +24,9 @@ const (
ExchangeService // exchange
OneDriveService // onedrive
SharePointService // sharepoint
ExchangeMetadataService // exchangeMetadata
OneDriveMetadataService // onedriveMetadata
SharePointMetadataService // sharepointMetadata
)
func toServiceType(service string) ServiceType {
@ -24,6 +37,12 @@ func toServiceType(service string) ServiceType {
return OneDriveService
case SharePointService.String():
return SharePointService
case ExchangeMetadataService.String():
return ExchangeMetadataService
case OneDriveMetadataService.String():
return OneDriveMetadataService
case SharePointMetadataService.String():
return SharePointMetadataService
default:
return UnknownService
}
@ -31,6 +50,10 @@ func toServiceType(service string) ServiceType {
var ErrorUnknownCategory = errors.New("unknown category string")
// CategoryType denotes what category of data the path corresponds to. The order
// of the enums below can be changed, but the string representation of each enum
// must remain the same or migration code needs to be added to handle changes to
// the string format.
type CategoryType int
//go:generate stringer -type=CategoryType -linecomment
@ -63,7 +86,8 @@ func ToCategoryType(category string) CategoryType {
}
}
// serviceCategories is a mapping of all valid service/category pairs.
// serviceCategories is a mapping of all valid service/category pairs for
// non-metadata paths.
var serviceCategories = map[ServiceType]map[CategoryType]struct{}{
ExchangeService: {
EmailCategory: {},

View File

@ -233,6 +233,101 @@ func (suite *DataLayerResourcePath) TestDir() {
}
}
func (suite *DataLayerResourcePath) TestToServiceCategoryMetadataPath() {
tenant := "a-tenant"
user := "a-user"
table := []struct {
name string
service path.ServiceType
category path.CategoryType
postfix []string
expectedService path.ServiceType
check assert.ErrorAssertionFunc
}{
{
name: "NoPostfixPasses",
service: path.ExchangeService,
category: path.EmailCategory,
expectedService: path.ExchangeMetadataService,
check: assert.NoError,
},
{
name: "PostfixPasses",
service: path.ExchangeService,
category: path.EmailCategory,
postfix: []string{"a", "b"},
expectedService: path.ExchangeMetadataService,
check: assert.NoError,
},
{
name: "Fails",
service: path.ExchangeService,
category: path.FilesCategory,
check: assert.Error,
},
{
name: "Passes",
service: path.ExchangeService,
category: path.ContactsCategory,
expectedService: path.ExchangeMetadataService,
check: assert.NoError,
},
{
name: "Passes",
service: path.ExchangeService,
category: path.EventsCategory,
expectedService: path.ExchangeMetadataService,
check: assert.NoError,
},
{
name: "Passes",
service: path.OneDriveService,
category: path.FilesCategory,
expectedService: path.OneDriveMetadataService,
check: assert.NoError,
},
{
name: "Passes",
service: path.SharePointService,
category: path.LibrariesCategory,
expectedService: path.SharePointMetadataService,
check: assert.NoError,
},
{
name: "Passes",
service: path.SharePointService,
category: path.ListsCategory,
expectedService: path.SharePointMetadataService,
check: assert.NoError,
},
}
for _, test := range table {
suite.T().Run(strings.Join([]string{
test.name,
test.service.String(),
test.category.String(),
}, "_"), func(t *testing.T) {
pb := path.Builder{}.Append(test.postfix...)
p, err := pb.ToServiceCategoryMetadataPath(
tenant,
user,
test.service,
test.category,
false,
)
test.check(t, err)
if err != nil {
return
}
assert.Equal(t, test.expectedService, p.Service())
})
}
}
func (suite *DataLayerResourcePath) TestToExchangePathForCategory() {
b := path.Builder{}.Append(rest...)
table := []struct {

View File

@ -12,11 +12,14 @@ func _() {
_ = x[ExchangeService-1]
_ = x[OneDriveService-2]
_ = x[SharePointService-3]
_ = x[ExchangeMetadataService-4]
_ = x[OneDriveMetadataService-5]
_ = x[SharePointMetadataService-6]
}
const _ServiceType_name = "UnknownServiceexchangeonedrivesharepoint"
const _ServiceType_name = "UnknownServiceexchangeonedrivesharepointexchangeMetadataonedriveMetadatasharepointMetadata"
var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40}
var _ServiceType_index = [...]uint8{0, 14, 22, 30, 40, 56, 72, 90}
func (i ServiceType) String() string {
if i < 0 || i >= ServiceType(len(_ServiceType_index)-1) {