distrubutes ServiceResource throughout path
Adds utilization of the ServiceResource struct to all of the path package functionality. In general, this takes most instances where a service and resource are requested, and replaces those values with a slice of ServiceResource tuples. To keep the review smaller, this change does not update any packages outside of path. Therefore the tests and build are guaranteed to fail. The next PR, which updates all other packages with the changes, is necessary for both PRs to move forward.
This commit is contained in:
parent
7224edd6f6
commit
c84b1b12ba
@ -203,28 +203,30 @@ func (pb Builder) withPrefix(elements ...string) *Builder {
|
||||
|
||||
// verifyPrefix ensures that the tenant and resourceOwner are valid
|
||||
// values, and that the builder has some directory structure.
|
||||
func (pb Builder) verifyPrefix(tenant, resourceOwner string) error {
|
||||
if err := verifyInputValues(tenant, resourceOwner); err != nil {
|
||||
return err
|
||||
}
|
||||
// func (pb Builder) verifyPrefix(tenant, resourceOwner string) error {
|
||||
// if err := verifyPrefixValues(tenant, resourceOwner); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if len(pb.elements) == 0 {
|
||||
return clues.New("missing path beyond prefix")
|
||||
}
|
||||
// if len(pb.elements) == 0 {
|
||||
// return clues.New("missing path beyond prefix")
|
||||
// }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Data Layer Path Transformers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func (pb Builder) ToStreamStorePath(
|
||||
tenant, purpose string,
|
||||
service ServiceType,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
if err := verifyInputValues(tenant, purpose); err != nil {
|
||||
cat := DetailsCategory
|
||||
|
||||
if err := verifyPrefixValues(tenant, srs, cat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -232,40 +234,18 @@ func (pb Builder) ToStreamStorePath(
|
||||
return nil, clues.New("missing path beyond prefix")
|
||||
}
|
||||
|
||||
metadataService := UnknownService
|
||||
dlrp := newDataLayerResourcePath(pb, tenant, toMetadataServices(srs), cat, isItem)
|
||||
|
||||
switch service {
|
||||
case ExchangeService:
|
||||
metadataService = ExchangeMetadataService
|
||||
case OneDriveService:
|
||||
metadataService = OneDriveMetadataService
|
||||
case SharePointService:
|
||||
metadataService = SharePointMetadataService
|
||||
}
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *pb.withPrefix(
|
||||
tenant,
|
||||
metadataService.String(),
|
||||
purpose,
|
||||
DetailsCategory.String()),
|
||||
service: metadataService,
|
||||
category: DetailsCategory,
|
||||
hasItem: isItem,
|
||||
}, nil
|
||||
return &dlrp, nil
|
||||
}
|
||||
|
||||
func (pb Builder) ToServiceCategoryMetadataPath(
|
||||
tenant, user string,
|
||||
service ServiceType,
|
||||
category CategoryType,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
cat CategoryType,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
if err := ValidateServiceAndCategory(service, category); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := verifyInputValues(tenant, user); err != nil {
|
||||
if err := verifyPrefixValues(tenant, srs, cat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -273,82 +253,69 @@ func (pb Builder) ToServiceCategoryMetadataPath(
|
||||
return nil, clues.New("missing path beyond prefix")
|
||||
}
|
||||
|
||||
metadataService := UnknownService
|
||||
dlrp := newDataLayerResourcePath(pb, tenant, toMetadataServices(srs), cat, isItem)
|
||||
|
||||
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
|
||||
return &dlrp, nil
|
||||
}
|
||||
|
||||
func (pb Builder) ToDataLayerPath(
|
||||
tenant, user string,
|
||||
service ServiceType,
|
||||
category CategoryType,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
cat CategoryType,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
if err := ValidateServiceAndCategory(service, category); err != nil {
|
||||
if err := verifyPrefixValues(tenant, srs, cat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := pb.verifyPrefix(tenant, user); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dlrp := newDataLayerResourcePath(pb, tenant, srs, cat, isItem)
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *pb.withPrefix(
|
||||
tenant,
|
||||
service.String(),
|
||||
user,
|
||||
category.String()),
|
||||
service: service,
|
||||
category: category,
|
||||
hasItem: isItem,
|
||||
}, nil
|
||||
return &dlrp, nil
|
||||
}
|
||||
|
||||
// TODO: remove this. https://github.com/alcionai/corso/issues/4025
|
||||
func (pb Builder) ToDataLayerExchangePathForCategory(
|
||||
tenant, user string,
|
||||
tenant, mailboxID string,
|
||||
category CategoryType,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
return pb.ToDataLayerPath(tenant, user, ExchangeService, category, isItem)
|
||||
srs, err := NewServiceResources(ExchangeService, mailboxID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pb.ToDataLayerPath(tenant, srs, category, isItem)
|
||||
}
|
||||
|
||||
// TODO: remove this. https://github.com/alcionai/corso/issues/4025
|
||||
func (pb Builder) ToDataLayerOneDrivePath(
|
||||
tenant, user string,
|
||||
tenant, userID string,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
return pb.ToDataLayerPath(tenant, user, OneDriveService, FilesCategory, isItem)
|
||||
srs, err := NewServiceResources(OneDriveService, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pb.ToDataLayerPath(tenant, srs, FilesCategory, isItem)
|
||||
}
|
||||
|
||||
// TODO: remove this. https://github.com/alcionai/corso/issues/4025
|
||||
func (pb Builder) ToDataLayerSharePointPath(
|
||||
tenant, site string,
|
||||
tenant, siteID string,
|
||||
category CategoryType,
|
||||
isItem bool,
|
||||
) (Path, error) {
|
||||
return pb.ToDataLayerPath(tenant, site, SharePointService, category, isItem)
|
||||
srs, err := NewServiceResources(SharePointService, siteID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pb.ToDataLayerPath(tenant, srs, category, isItem)
|
||||
}
|
||||
|
||||
// TODO: ToDataLayerGroupsPath()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Stringers and PII Concealer Compliance
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@ -46,7 +46,10 @@ func (suite *BuilderUnitSuite) TestAppend() {
|
||||
func (suite *BuilderUnitSuite) TestAppendItem() {
|
||||
t := suite.T()
|
||||
|
||||
p, err := Build("t", "ro", ExchangeService, EmailCategory, false, "foo", "bar")
|
||||
srs, err := NewServiceResources(ExchangeService, "ro")
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
p, err := Build("t", srs, EmailCategory, false, "foo", "bar")
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
pb := p.ToBuilder()
|
||||
@ -339,8 +342,13 @@ func (suite *BuilderUnitSuite) TestFolder() {
|
||||
}
|
||||
|
||||
func (suite *BuilderUnitSuite) TestPIIHandling() {
|
||||
p, err := Build("t", "ro", ExchangeService, EventsCategory, true, "dir", "item")
|
||||
require.NoError(suite.T(), err)
|
||||
t := suite.T()
|
||||
|
||||
srs, err := NewServiceResources(ExchangeService, "ro")
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
p, err := Build("t", srs, EventsCategory, true, "dir", "item")
|
||||
require.NoError(t, err)
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
|
||||
@ -75,24 +75,6 @@ var serviceCategories = map[ServiceType]map[CategoryType]struct{}{
|
||||
},
|
||||
}
|
||||
|
||||
func validateServiceAndCategoryStrings(s, c string) (ServiceType, CategoryType, error) {
|
||||
service := toServiceType(s)
|
||||
if service == UnknownService {
|
||||
return UnknownService, UnknownCategory, clues.Stack(ErrorUnknownService).With("service", fmt.Sprintf("%q", s))
|
||||
}
|
||||
|
||||
category := ToCategoryType(c)
|
||||
if category == UnknownCategory {
|
||||
return UnknownService, UnknownCategory, clues.Stack(ErrorUnknownService).With("category", fmt.Sprintf("%q", c))
|
||||
}
|
||||
|
||||
if err := ValidateServiceAndCategory(service, category); err != nil {
|
||||
return UnknownService, UnknownCategory, err
|
||||
}
|
||||
|
||||
return service, category, nil
|
||||
}
|
||||
|
||||
func ValidateServiceAndCategory(service ServiceType, category CategoryType) error {
|
||||
cats, ok := serviceCategories[service]
|
||||
if !ok {
|
||||
|
||||
@ -59,7 +59,10 @@ func (suite *OneDrivePathSuite) Test_ToOneDrivePath() {
|
||||
suite.Run(tt.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
p, err := path.Build("tenant", "user", path.OneDriveService, path.FilesCategory, false, tt.pathElements...)
|
||||
srs, err := path.NewServiceResources(path.OneDriveService, "user")
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
p, err := path.Build("tenant", srs, path.FilesCategory, false, tt.pathElements...)
|
||||
require.NoError(suite.T(), err, clues.ToCore(err))
|
||||
|
||||
got, err := path.ToDrivePath(p)
|
||||
|
||||
@ -79,10 +79,12 @@ var (
|
||||
// string.
|
||||
type Path interface {
|
||||
String() string
|
||||
Service() ServiceType
|
||||
// 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
|
||||
Category() CategoryType
|
||||
Tenant() string
|
||||
ResourceOwner() string
|
||||
Folder(escaped bool) string
|
||||
Folders() Elements
|
||||
Item() string
|
||||
@ -132,41 +134,29 @@ type RestorePaths struct {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func Build(
|
||||
tenant, resourceOwner string,
|
||||
service ServiceType,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
category CategoryType,
|
||||
hasItem bool,
|
||||
elements ...string,
|
||||
) (Path, error) {
|
||||
b := Builder{}.Append(elements...)
|
||||
|
||||
return b.ToDataLayerPath(
|
||||
tenant, resourceOwner,
|
||||
service, category,
|
||||
hasItem)
|
||||
return Builder{}.
|
||||
Append(elements...).
|
||||
ToDataLayerPath(tenant, srs, category, hasItem)
|
||||
}
|
||||
|
||||
func BuildPrefix(
|
||||
tenant, resourceOwner string,
|
||||
s ServiceType,
|
||||
c CategoryType,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
cat CategoryType,
|
||||
) (Path, error) {
|
||||
pb := Builder{}
|
||||
|
||||
if err := ValidateServiceAndCategory(s, c); err != nil {
|
||||
if err := verifyPrefixValues(tenant, srs, cat); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := verifyInputValues(tenant, resourceOwner); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dlrp := newDataLayerResourcePath(Builder{}, tenant, srs, cat, false)
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *pb.withPrefix(tenant, s.String(), resourceOwner, c.String()),
|
||||
service: s,
|
||||
category: c,
|
||||
hasItem: false,
|
||||
}, nil
|
||||
return &dlrp, nil
|
||||
}
|
||||
|
||||
// FromDataLayerPath parses the escaped path p, validates the elements in p
|
||||
@ -186,24 +176,37 @@ func FromDataLayerPath(p string, isItem bool) (Path, error) {
|
||||
return nil, clues.Stack(errParsingPath, err).With("path_string", p)
|
||||
}
|
||||
|
||||
// initial check for minimum required elements:
|
||||
// tenant, service, resource, category, container/item
|
||||
if len(pb.elements) < 5 {
|
||||
return nil, clues.New("path has too few segments").With("path_string", p)
|
||||
}
|
||||
|
||||
service, category, err := validateServiceAndCategoryStrings(
|
||||
pb.elements[1],
|
||||
pb.elements[3],
|
||||
)
|
||||
srs, catIdx, err := ElementsToServiceResources(pb.elements[1:])
|
||||
if err != nil {
|
||||
return nil, clues.Stack(err)
|
||||
}
|
||||
|
||||
// follow-up check: if more than one service exists, revisit the len check.
|
||||
if len(srs) > 1 && len(pb.elements) < 3+(2*len(srs)) {
|
||||
return nil, clues.New("path has too few segments").With("path_string", p)
|
||||
}
|
||||
|
||||
// +1 to account for slicing the tenant when calling the transformer func.
|
||||
category := ToCategoryType(pb.elements[catIdx+1])
|
||||
|
||||
if err := verifyPrefixValues(pb.elements[0], srs, category); err != nil {
|
||||
return nil, clues.Stack(errParsingPath, err).With("path_string", p)
|
||||
}
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *pb,
|
||||
service: service,
|
||||
category: category,
|
||||
hasItem: isItem,
|
||||
}, nil
|
||||
dlrp := dataLayerResourcePath{
|
||||
Builder: *pb,
|
||||
serviceResources: srs,
|
||||
category: category,
|
||||
hasItem: isItem,
|
||||
}
|
||||
|
||||
return &dlrp, nil
|
||||
}
|
||||
|
||||
// TrimTrailingSlash takes an escaped path element and returns an escaped path
|
||||
@ -290,16 +293,21 @@ func Split(segment string) []string {
|
||||
// Unexported Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
func verifyInputValues(tenant, resourceOwner string) error {
|
||||
func verifyPrefixValues(
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
cat CategoryType,
|
||||
) error {
|
||||
if len(tenant) == 0 {
|
||||
return clues.Stack(errMissingSegment, clues.New("tenant"))
|
||||
}
|
||||
|
||||
if len(resourceOwner) == 0 {
|
||||
return clues.Stack(errMissingSegment, clues.New("resourceOwner"))
|
||||
if err := validateServiceResources(srs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
// only the final service is checked for its category validity
|
||||
return ValidateServiceAndCategory(srs[len(srs)-1].Service, cat)
|
||||
}
|
||||
|
||||
// escapeElement takes a single path element and escapes all characters that
|
||||
|
||||
@ -334,14 +334,12 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
testUser,
|
||||
testElement1,
|
||||
testElement2,
|
||||
testElement3,
|
||||
),
|
||||
testElement3),
|
||||
expectedFolder: fmt.Sprintf(
|
||||
"%s/%s/%s",
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
testElement3,
|
||||
),
|
||||
testElement3),
|
||||
expectedSplit: []string{
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
@ -351,8 +349,7 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
expectedItemFolder: fmt.Sprintf(
|
||||
"%s/%s",
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
),
|
||||
testElement2),
|
||||
expectedItemSplit: []string{
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
@ -366,14 +363,12 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
testUser,
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
testElement3,
|
||||
),
|
||||
testElement3),
|
||||
expectedFolder: fmt.Sprintf(
|
||||
"%s/%s/%s",
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
testElement3,
|
||||
),
|
||||
testElement3),
|
||||
expectedSplit: []string{
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
@ -383,8 +378,7 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
expectedItemFolder: fmt.Sprintf(
|
||||
"%s/%s",
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
),
|
||||
testElement2),
|
||||
expectedItemSplit: []string{
|
||||
testElementTrimmed,
|
||||
testElement2,
|
||||
@ -393,21 +387,27 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
}
|
||||
|
||||
for service, cats := range serviceCategories {
|
||||
|
||||
for cat := range cats {
|
||||
|
||||
for _, item := range isItem {
|
||||
suite.Run(fmt.Sprintf("%s-%s-%s", service, cat, item.name), func() {
|
||||
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
testPath := fmt.Sprintf(test.unescapedPath, service, cat)
|
||||
var (
|
||||
t = suite.T()
|
||||
testPath = fmt.Sprintf(test.unescapedPath, service, cat)
|
||||
sr = ServiceResource{service, testUser}
|
||||
)
|
||||
|
||||
p, err := FromDataLayerPath(testPath, item.isItem)
|
||||
require.NoError(t, err, clues.ToCore(err))
|
||||
|
||||
assert.Equal(t, service, p.Service(), "service")
|
||||
assert.Len(t, p.ServiceResources(), 1, "service resources")
|
||||
assert.Equal(t, sr, p.ServiceResources()[0], "service resource")
|
||||
assert.Equal(t, cat, p.Category(), "category")
|
||||
assert.Equal(t, testTenant, p.Tenant(), "tenant")
|
||||
assert.Equal(t, testUser, p.ResourceOwner(), "resource owner")
|
||||
|
||||
fld := p.Folder(false)
|
||||
escfld := p.Folder(true)
|
||||
@ -435,44 +435,74 @@ func (suite *PathUnitSuite) TestFromDataLayerPath() {
|
||||
func (suite *PathUnitSuite) TestBuildPrefix() {
|
||||
table := []struct {
|
||||
name string
|
||||
service ServiceType
|
||||
category CategoryType
|
||||
tenant string
|
||||
owner string
|
||||
srs []ServiceResource
|
||||
category CategoryType
|
||||
expect string
|
||||
expectErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "ok",
|
||||
service: ExchangeService,
|
||||
category: ContactsCategory,
|
||||
tenant: "t",
|
||||
owner: "ro",
|
||||
expect: join([]string{"t", ExchangeService.String(), "ro", ContactsCategory.String()}),
|
||||
srs: []ServiceResource{{ExchangeService, "roo"}},
|
||||
category: ContactsCategory,
|
||||
expect: join([]string{"t", ExchangeService.String(), "roo", ContactsCategory.String()}),
|
||||
expectErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "ok with subservice",
|
||||
tenant: "t",
|
||||
srs: []ServiceResource{
|
||||
{GroupsService, "roo"},
|
||||
{SharePointService, "oor"},
|
||||
},
|
||||
category: LibrariesCategory,
|
||||
expect: join([]string{
|
||||
"t",
|
||||
GroupsService.String(), "roo",
|
||||
SharePointService.String(), "oor",
|
||||
LibrariesCategory.String()}),
|
||||
expectErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "bad category",
|
||||
service: ExchangeService,
|
||||
srs: []ServiceResource{{ExchangeService, "roo"}},
|
||||
category: FilesCategory,
|
||||
tenant: "t",
|
||||
owner: "ro",
|
||||
expectErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "bad tenant",
|
||||
service: ExchangeService,
|
||||
category: ContactsCategory,
|
||||
tenant: "",
|
||||
owner: "ro",
|
||||
srs: []ServiceResource{{ExchangeService, "roo"}},
|
||||
category: ContactsCategory,
|
||||
expectErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "bad owner",
|
||||
service: ExchangeService,
|
||||
category: ContactsCategory,
|
||||
name: "bad resource",
|
||||
tenant: "t",
|
||||
owner: "",
|
||||
srs: []ServiceResource{{ExchangeService, ""}},
|
||||
category: ContactsCategory,
|
||||
expectErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "bad subservice",
|
||||
tenant: "t",
|
||||
srs: []ServiceResource{
|
||||
{ExchangeService, "roo"},
|
||||
{OneDriveService, "oor"},
|
||||
},
|
||||
category: FilesCategory,
|
||||
expectErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "bad subservice resource",
|
||||
tenant: "t",
|
||||
srs: []ServiceResource{
|
||||
{GroupsService, "roo"},
|
||||
{SharePointService, ""},
|
||||
},
|
||||
category: LibrariesCategory,
|
||||
expectErr: require.Error,
|
||||
},
|
||||
}
|
||||
@ -480,7 +510,7 @@ func (suite *PathUnitSuite) TestBuildPrefix() {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
r, err := BuildPrefix(test.tenant, test.owner, test.service, test.category)
|
||||
r, err := BuildPrefix(test.tenant, test.srs, test.category)
|
||||
test.expectErr(t, err, clues.ToCore(err))
|
||||
|
||||
if r == nil {
|
||||
|
||||
@ -18,9 +18,28 @@ import (
|
||||
// element after the prefix.
|
||||
type dataLayerResourcePath struct {
|
||||
Builder
|
||||
category CategoryType
|
||||
service ServiceType
|
||||
hasItem bool
|
||||
category CategoryType
|
||||
serviceResources []ServiceResource
|
||||
hasItem bool
|
||||
}
|
||||
|
||||
// performs no validation, assumes the caller has validated the inputs.
|
||||
func newDataLayerResourcePath(
|
||||
pb Builder,
|
||||
tenant string,
|
||||
srs []ServiceResource,
|
||||
cat CategoryType,
|
||||
isItem bool,
|
||||
) dataLayerResourcePath {
|
||||
pfx := append([]string{tenant}, ServiceResourcesToElements(srs)...)
|
||||
pfx = append(pfx, cat.String())
|
||||
|
||||
return dataLayerResourcePath{
|
||||
Builder: *pb.withPrefix(pfx...),
|
||||
serviceResources: srs,
|
||||
category: cat,
|
||||
hasItem: isItem,
|
||||
}
|
||||
}
|
||||
|
||||
// Tenant returns the tenant ID embedded in the dataLayerResourcePath.
|
||||
@ -28,9 +47,8 @@ func (rp dataLayerResourcePath) Tenant() string {
|
||||
return rp.Builder.elements[0]
|
||||
}
|
||||
|
||||
// Service returns the ServiceType embedded in the dataLayerResourcePath.
|
||||
func (rp dataLayerResourcePath) Service() ServiceType {
|
||||
return rp.service
|
||||
func (rp dataLayerResourcePath) ServiceResources() []ServiceResource {
|
||||
return rp.serviceResources
|
||||
}
|
||||
|
||||
// Category returns the CategoryType embedded in the dataLayerResourcePath.
|
||||
@ -95,15 +113,18 @@ func (rp dataLayerResourcePath) Item() string {
|
||||
// Dir removes the last element from the path. If this would remove a
|
||||
// value that is part of the standard prefix structure, an error is returned.
|
||||
func (rp dataLayerResourcePath) Dir() (Path, error) {
|
||||
if len(rp.elements) <= 4 {
|
||||
// Dir is not allowed to slice off any prefix values.
|
||||
// The prefix len is determined by the length of the number of
|
||||
// service+resource tuples, plus 2 (tenant and category).
|
||||
if len(rp.elements) <= 2+(2*len(rp.serviceResources)) {
|
||||
return nil, clues.New("unable to shorten path").With("path", rp)
|
||||
}
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *rp.Builder.Dir(),
|
||||
service: rp.service,
|
||||
category: rp.category,
|
||||
hasItem: false,
|
||||
Builder: *rp.Builder.Dir(),
|
||||
serviceResources: rp.serviceResources,
|
||||
category: rp.category,
|
||||
hasItem: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -116,10 +137,10 @@ func (rp dataLayerResourcePath) Append(
|
||||
}
|
||||
|
||||
return &dataLayerResourcePath{
|
||||
Builder: *rp.Builder.Append(elems...),
|
||||
service: rp.service,
|
||||
category: rp.category,
|
||||
hasItem: isItem,
|
||||
Builder: *rp.Builder.Append(elems...),
|
||||
serviceResources: rp.serviceResources,
|
||||
category: rp.category,
|
||||
hasItem: isItem,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -256,10 +256,10 @@ func (suite *DataLayerResourcePath) TestDir() {
|
||||
|
||||
func (suite *DataLayerResourcePath) TestToServiceCategoryMetadataPath() {
|
||||
tenant := "a-tenant"
|
||||
user := "a-user"
|
||||
resource := "a-resource"
|
||||
table := []struct {
|
||||
name string
|
||||
service path.ServiceType
|
||||
srs []path.ServiceResource
|
||||
category path.CategoryType
|
||||
postfix []string
|
||||
expectedService path.ServiceType
|
||||
@ -267,14 +267,14 @@ func (suite *DataLayerResourcePath) TestToServiceCategoryMetadataPath() {
|
||||
}{
|
||||
{
|
||||
name: "NoPostfixPasses",
|
||||
service: path.ExchangeService,
|
||||
srs: []path.ServiceResource{{path.ExchangeService, resource}},
|
||||
category: path.EmailCategory,
|
||||
expectedService: path.ExchangeMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "PostfixPasses",
|
||||
service: path.ExchangeService,
|
||||
srs: []path.ServiceResource{{path.ExchangeService, resource}},
|
||||
category: path.EmailCategory,
|
||||
postfix: []string{"a", "b"},
|
||||
expectedService: path.ExchangeMetadataService,
|
||||
@ -282,48 +282,48 @@ func (suite *DataLayerResourcePath) TestToServiceCategoryMetadataPath() {
|
||||
},
|
||||
{
|
||||
name: "Fails",
|
||||
service: path.ExchangeService,
|
||||
srs: []path.ServiceResource{{path.ExchangeService, resource}},
|
||||
category: path.FilesCategory,
|
||||
check: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.ExchangeService,
|
||||
srs: []path.ServiceResource{{path.ExchangeService, resource}},
|
||||
category: path.ContactsCategory,
|
||||
expectedService: path.ExchangeMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.ExchangeService,
|
||||
srs: []path.ServiceResource{{path.ExchangeService, resource}},
|
||||
category: path.EventsCategory,
|
||||
expectedService: path.ExchangeMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.OneDriveService,
|
||||
srs: []path.ServiceResource{{path.OneDriveService, resource}},
|
||||
category: path.FilesCategory,
|
||||
expectedService: path.OneDriveMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.SharePointService,
|
||||
srs: []path.ServiceResource{{path.SharePointService, resource}},
|
||||
category: path.LibrariesCategory,
|
||||
expectedService: path.SharePointMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.SharePointService,
|
||||
srs: []path.ServiceResource{{path.SharePointService, resource}},
|
||||
category: path.ListsCategory,
|
||||
expectedService: path.SharePointMetadataService,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "Passes",
|
||||
service: path.SharePointService,
|
||||
srs: []path.ServiceResource{{path.SharePointService, resource}},
|
||||
category: path.PagesCategory,
|
||||
expectedService: path.SharePointMetadataService,
|
||||
check: assert.NoError,
|
||||
@ -331,27 +331,26 @@ func (suite *DataLayerResourcePath) TestToServiceCategoryMetadataPath() {
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
suite.Run(strings.Join([]string{
|
||||
name := strings.Join([]string{
|
||||
test.name,
|
||||
test.service.String(),
|
||||
test.srs[0].Service.String(),
|
||||
test.category.String(),
|
||||
}, "_"), func() {
|
||||
}, "_")
|
||||
|
||||
suite.Run(name, func() {
|
||||
t := suite.T()
|
||||
pb := path.Builder{}.Append(test.postfix...)
|
||||
|
||||
p, err := pb.ToServiceCategoryMetadataPath(
|
||||
tenant,
|
||||
user,
|
||||
test.service,
|
||||
test.srs,
|
||||
test.category,
|
||||
false)
|
||||
test.check(t, err, clues.ToCore(err))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
if err == nil {
|
||||
assert.Equal(t, test.expectedService, p.ServiceResources()[0])
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expectedService, p.Service())
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -402,9 +401,9 @@ func (suite *DataLayerResourcePath) TestToExchangePathForCategory() {
|
||||
}
|
||||
|
||||
assert.Equal(t, testTenant, p.Tenant())
|
||||
assert.Equal(t, path.ExchangeService, p.Service())
|
||||
assert.Equal(t, path.ExchangeService, p.ServiceResources()[0].Service)
|
||||
assert.Equal(t, test.category, p.Category())
|
||||
assert.Equal(t, testUser, p.ResourceOwner())
|
||||
assert.Equal(t, testUser, p.ServiceResources()[0].ProtectedResource)
|
||||
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())
|
||||
@ -456,7 +455,10 @@ func (suite *PopulatedDataLayerResourcePath) TestService() {
|
||||
suite.Run(m.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
assert.Equal(t, path.ExchangeService, suite.paths[m.isItem].Service())
|
||||
assert.Equal(
|
||||
t,
|
||||
path.ExchangeService,
|
||||
suite.paths[m.isItem].ServiceResources()[0].Service)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -476,7 +478,10 @@ func (suite *PopulatedDataLayerResourcePath) TestResourceOwner() {
|
||||
suite.Run(m.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
assert.Equal(t, testUser, suite.paths[m.isItem].ResourceOwner())
|
||||
assert.Equal(
|
||||
t,
|
||||
testUser,
|
||||
suite.paths[m.isItem].ServiceResources()[0].ProtectedResource)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,118 +20,76 @@ func TestServiceCategoryUnitSuite(t *testing.T) {
|
||||
suite.Run(t, s)
|
||||
}
|
||||
|
||||
func (suite *ServiceCategoryUnitSuite) TestValidateServiceAndCategoryBadStringErrors() {
|
||||
func (suite *ServiceCategoryUnitSuite) TestVerifyPrefixValues() {
|
||||
table := []struct {
|
||||
name string
|
||||
service string
|
||||
category string
|
||||
}{
|
||||
{
|
||||
name: "Service",
|
||||
service: "foo",
|
||||
category: EmailCategory.String(),
|
||||
},
|
||||
{
|
||||
name: "Category",
|
||||
service: ExchangeService.String(),
|
||||
category: "foo",
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
_, _, err := validateServiceAndCategoryStrings(test.service, test.category)
|
||||
assert.Error(suite.T(), err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ServiceCategoryUnitSuite) TestValidateServiceAndCategory() {
|
||||
table := []struct {
|
||||
name string
|
||||
service string
|
||||
category string
|
||||
expectedService ServiceType
|
||||
expectedCategory CategoryType
|
||||
check assert.ErrorAssertionFunc
|
||||
service ServiceType
|
||||
category CategoryType
|
||||
check assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "UnknownService",
|
||||
service: UnknownService.String(),
|
||||
category: EmailCategory.String(),
|
||||
service: UnknownService,
|
||||
category: EmailCategory,
|
||||
check: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "UnknownCategory",
|
||||
service: ExchangeService.String(),
|
||||
category: UnknownCategory.String(),
|
||||
service: ExchangeService,
|
||||
category: UnknownCategory,
|
||||
check: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "BadServiceString",
|
||||
service: "foo",
|
||||
category: EmailCategory.String(),
|
||||
name: "BadServiceType",
|
||||
service: ServiceType(-1),
|
||||
category: EmailCategory,
|
||||
check: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "BadCategoryString",
|
||||
service: ExchangeService.String(),
|
||||
category: "foo",
|
||||
name: "BadCategoryType",
|
||||
service: ExchangeService,
|
||||
category: CategoryType(-1),
|
||||
check: assert.Error,
|
||||
},
|
||||
{
|
||||
name: "ExchangeEmail",
|
||||
service: ExchangeService.String(),
|
||||
category: EmailCategory.String(),
|
||||
expectedService: ExchangeService,
|
||||
expectedCategory: EmailCategory,
|
||||
check: assert.NoError,
|
||||
name: "ExchangeEmail",
|
||||
service: ExchangeService,
|
||||
category: EmailCategory,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "ExchangeContacts",
|
||||
service: ExchangeService.String(),
|
||||
category: ContactsCategory.String(),
|
||||
expectedService: ExchangeService,
|
||||
expectedCategory: ContactsCategory,
|
||||
check: assert.NoError,
|
||||
name: "ExchangeContacts",
|
||||
service: ExchangeService,
|
||||
category: ContactsCategory,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "ExchangeEvents",
|
||||
service: ExchangeService.String(),
|
||||
category: EventsCategory.String(),
|
||||
expectedService: ExchangeService,
|
||||
expectedCategory: EventsCategory,
|
||||
check: assert.NoError,
|
||||
name: "ExchangeEvents",
|
||||
service: ExchangeService,
|
||||
category: EventsCategory,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "OneDriveFiles",
|
||||
service: OneDriveService.String(),
|
||||
category: FilesCategory.String(),
|
||||
expectedService: OneDriveService,
|
||||
expectedCategory: FilesCategory,
|
||||
check: assert.NoError,
|
||||
name: "OneDriveFiles",
|
||||
service: OneDriveService,
|
||||
category: FilesCategory,
|
||||
check: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "SharePointLibraries",
|
||||
service: SharePointService.String(),
|
||||
category: LibrariesCategory.String(),
|
||||
expectedService: SharePointService,
|
||||
expectedCategory: LibrariesCategory,
|
||||
check: assert.NoError,
|
||||
name: "SharePointLibraries",
|
||||
service: SharePointService,
|
||||
category: LibrariesCategory,
|
||||
check: assert.NoError,
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
s, c, err := validateServiceAndCategoryStrings(test.service, test.category)
|
||||
srs := []ServiceResource{{test.service, "resource"}}
|
||||
|
||||
err := verifyPrefixValues("tid", srs, test.category)
|
||||
test.check(t, err, clues.ToCore(err))
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
assert.Equal(t, test.expectedService, s)
|
||||
assert.Equal(t, test.expectedCategory, c)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +85,40 @@ func ServiceResourcesToElements(srs []ServiceResource) Elements {
|
||||
return es
|
||||
}
|
||||
|
||||
// ElementsToServiceResources turns as many pairs of elems as possible
|
||||
// into ServiceResource tuples. Elems must begin with a service, but
|
||||
// may contain more entries than there are service/resource pairs.
|
||||
// This transformer will continue consuming elements until it finds an
|
||||
// even-numbered index that cannot be cast to a ServiceType.
|
||||
// Returns the serviceResource pairs, the first index containing element
|
||||
// that is not part of a service/resource pair, and an error if elems is
|
||||
// len==0 or contains no services.
|
||||
func ElementsToServiceResources(elems Elements) ([]ServiceResource, int, error) {
|
||||
if len(elems) == 0 {
|
||||
return nil, -1, clues.Wrap(errMissingSegment, "service")
|
||||
}
|
||||
|
||||
var (
|
||||
srs = make([]ServiceResource, 0)
|
||||
i int
|
||||
)
|
||||
|
||||
for j := 1; i < len(elems); i, j = i+2, j+2 {
|
||||
service := toServiceType(elems[i])
|
||||
if service == UnknownService {
|
||||
if i == 0 {
|
||||
return nil, -1, clues.Wrap(errMissingSegment, "service")
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
srs = append(srs, ServiceResource{service, elems[j]})
|
||||
}
|
||||
|
||||
return srs, i, nil
|
||||
}
|
||||
|
||||
// checks for the following:
|
||||
// 1. each ServiceResource is valid
|
||||
// 2. if len(srs) > 1, srs[i], srs[i+1] pass subservice checks.
|
||||
@ -112,3 +146,29 @@ func validateServiceResources(srs []ServiceResource) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// makes a copy of the slice with all of the Services swapped for their
|
||||
// metadata service countterparts.
|
||||
func toMetadataServices(srs []ServiceResource) []ServiceResource {
|
||||
msrs := make([]ServiceResource, 0, len(srs))
|
||||
|
||||
for _, sr := range srs {
|
||||
msr := sr
|
||||
metadataService := UnknownService
|
||||
|
||||
switch sr.Service {
|
||||
case ExchangeService:
|
||||
metadataService = ExchangeMetadataService
|
||||
case OneDriveService:
|
||||
metadataService = OneDriveMetadataService
|
||||
case SharePointService:
|
||||
metadataService = SharePointMetadataService
|
||||
// TODO: add groups
|
||||
}
|
||||
|
||||
msr.Service = metadataService
|
||||
msrs = append(msrs, msr)
|
||||
}
|
||||
|
||||
return msrs
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ func (suite *ServiceResourceUnitSuite) TestValidateServiceResources() {
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ServiceResourceUnitSuite) TestServiceResourceElements() {
|
||||
func (suite *ServiceResourceUnitSuite) TestServiceResourceToElements() {
|
||||
table := []struct {
|
||||
name string
|
||||
srs []ServiceResource
|
||||
@ -164,3 +164,80 @@ func (suite *ServiceResourceUnitSuite) TestServiceResourceElements() {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (suite *ServiceResourceUnitSuite) TestElementsToServiceResource() {
|
||||
table := []struct {
|
||||
name string
|
||||
elems Elements
|
||||
expectErr assert.ErrorAssertionFunc
|
||||
expectIdx int
|
||||
expectSRS []ServiceResource
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
elems: Elements{},
|
||||
expectErr: assert.Error,
|
||||
expectIdx: -1,
|
||||
expectSRS: nil,
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
elems: nil,
|
||||
expectErr: assert.Error,
|
||||
expectIdx: -1,
|
||||
expectSRS: nil,
|
||||
},
|
||||
{
|
||||
name: "non-service 0th elem",
|
||||
elems: Elements{"fnords"},
|
||||
expectErr: assert.Error,
|
||||
expectIdx: -1,
|
||||
expectSRS: nil,
|
||||
},
|
||||
{
|
||||
name: "non-service 2nd elem",
|
||||
elems: Elements{ExchangeService.String(), "fnords", "smarf"},
|
||||
expectErr: assert.Error,
|
||||
expectIdx: -1,
|
||||
expectSRS: nil,
|
||||
},
|
||||
{
|
||||
name: "single serviceResource",
|
||||
elems: Elements{ExchangeService.String(), "fnords"},
|
||||
expectErr: assert.NoError,
|
||||
expectIdx: 2,
|
||||
expectSRS: []ServiceResource{{ExchangeService, "fnords"}},
|
||||
},
|
||||
{
|
||||
name: "single serviceResource and extra value",
|
||||
elems: Elements{ExchangeService.String(), "fnords", "smarf"},
|
||||
expectErr: assert.NoError,
|
||||
expectIdx: 2,
|
||||
expectSRS: []ServiceResource{{ExchangeService, "fnords"}},
|
||||
},
|
||||
{
|
||||
name: "multiple serviceResource",
|
||||
elems: Elements{ExchangeService.String(), "fnords", OneDriveService.String(), "smarf"},
|
||||
expectErr: assert.NoError,
|
||||
expectIdx: 4,
|
||||
expectSRS: []ServiceResource{{ExchangeService, "fnords"}, {OneDriveService, "smarf"}},
|
||||
},
|
||||
{
|
||||
name: "multiple serviceResource and extra value",
|
||||
elems: Elements{ExchangeService.String(), "fnords", OneDriveService.String(), "smarf", "flaboigans"},
|
||||
expectErr: assert.NoError,
|
||||
expectIdx: 4,
|
||||
expectSRS: []ServiceResource{{ExchangeService, "fnords"}, {OneDriveService, "smarf"}},
|
||||
},
|
||||
}
|
||||
for _, test := range table {
|
||||
suite.Run(test.name, func() {
|
||||
t := suite.T()
|
||||
|
||||
srs, idx, err := ElementsToServiceResources(test.elems)
|
||||
test.expectErr(t, err, clues.ToCore(err))
|
||||
assert.Equal(t, test.expectIdx, idx)
|
||||
assert.Equal(t, test.expectSRS, srs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user