clean out scope value chaff (#731)

This commit is contained in:
Keepers 2022-09-09 19:21:31 -06:00 committed by GitHub
parent 25ce11b2c6
commit a226035c23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 116 additions and 115 deletions

View File

@ -92,7 +92,7 @@ func (b Backup) MinimumPrintable() any {
StartedAt: b.StartedAt, StartedAt: b.StartedAt,
Status: b.Status, Status: b.Status,
Version: "0", Version: "0",
Selectors: b.Selectors.Printable(), Selectors: b.Selectors.ToPrintable(),
} }
} }
@ -117,6 +117,6 @@ func (b Backup) Values() []string {
common.FormatTime(b.StartedAt), common.FormatTime(b.StartedAt),
string(b.ID), string(b.ID),
status, status,
b.Selectors.Printable().Resources(), b.Selectors.ToPrintable().Resources(),
} }
} }

View File

@ -90,7 +90,7 @@ func (suite *BackupSuite) TestBackup_MinimumPrintable() {
assert.Equal(t, now, result.StartedAt, "started at") assert.Equal(t, now, result.StartedAt, "started at")
assert.Equal(t, b.Status, result.Status, "status") assert.Equal(t, b.Status, result.Status, "status")
bselp := b.Selectors.Printable() bselp := b.Selectors.ToPrintable()
assert.Equal(t, bselp, result.Selectors, "selectors") assert.Equal(t, bselp, result.Selectors, "selectors")
assert.Equal(t, bselp.Resources(), result.Selectors.Resources(), "selector resources") assert.Equal(t, bselp.Resources(), result.Selectors.Resources(), "selector resources")
} }

View File

@ -80,6 +80,11 @@ func (s Selector) ToExchangeRestore() (*ExchangeRestore, error) {
return &src, nil return &src, nil
} }
// Printable creates the minimized display of a selector, formatted for human readability.
func (s exchange) Printable() Printable {
return toPrintable[ExchangeScope](s.Selector)
}
// ------------------- // -------------------
// Exclude/Includes // Exclude/Includes
@ -169,7 +174,7 @@ func (s *exchange) Contacts(users, folders, contacts []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Item, ExchangeContact, users, contacts). makeScope[ExchangeScope](ExchangeContact, users, contacts).
set(ExchangeContactFolder, folders), set(ExchangeContactFolder, folders),
) )
@ -185,7 +190,7 @@ func (s *exchange) ContactFolders(users, folders []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Group, ExchangeContactFolder, users, folders), makeScope[ExchangeScope](ExchangeContactFolder, users, folders),
) )
return scopes return scopes
@ -200,7 +205,7 @@ func (s *exchange) Events(users, calendars, events []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Item, ExchangeEvent, users, events). makeScope[ExchangeScope](ExchangeEvent, users, events).
set(ExchangeEventCalendar, calendars), set(ExchangeEventCalendar, calendars),
) )
@ -217,7 +222,7 @@ func (s *exchange) EventCalendars(users, events []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Group, ExchangeEventCalendar, users, events), makeScope[ExchangeScope](ExchangeEventCalendar, users, events),
) )
return scopes return scopes
@ -232,7 +237,7 @@ func (s *exchange) Mails(users, folders, mails []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Item, ExchangeMail, users, mails). makeScope[ExchangeScope](ExchangeMail, users, mails).
set(ExchangeMailFolder, folders), set(ExchangeMailFolder, folders),
) )
@ -248,7 +253,7 @@ func (s *exchange) MailFolders(users, folders []string) []ExchangeScope {
scopes = append( scopes = append(
scopes, scopes,
makeScope[ExchangeScope](Group, ExchangeMailFolder, users, folders), makeScope[ExchangeScope](ExchangeMailFolder, users, folders),
) )
return scopes return scopes
@ -263,9 +268,9 @@ func (s *exchange) Users(users []string) []ExchangeScope {
scopes := []ExchangeScope{} scopes := []ExchangeScope{}
scopes = append(scopes, scopes = append(scopes,
makeScope[ExchangeScope](Group, ExchangeContactFolder, users, Any()), makeScope[ExchangeScope](ExchangeContactFolder, users, Any()),
makeScope[ExchangeScope](Item, ExchangeEventCalendar, users, Any()), makeScope[ExchangeScope](ExchangeEventCalendar, users, Any()),
makeScope[ExchangeScope](Group, ExchangeMailFolder, users, Any()), makeScope[ExchangeScope](ExchangeMailFolder, users, Any()),
) )
return scopes return scopes
@ -618,12 +623,6 @@ func (s ExchangeScope) FilterCategory() exchangeCategory {
return exchangeCategory(getFilterCategory(s)) return exchangeCategory(getFilterCategory(s))
} }
// Granularity describes the granularity (directory || item)
// of the data in scope.
func (s ExchangeScope) Granularity() string {
return getGranularity(s)
}
// IncludeCategory checks whether the scope includes a certain category of data. // IncludeCategory checks whether the scope includes a certain category of data.
// Ex: to check if the scope includes mail data: // Ex: to check if the scope includes mail data:
// s.IncludesCategory(selector.ExchangeMail) // s.IncludesCategory(selector.ExchangeMail)

View File

@ -88,8 +88,7 @@ func (ms mockScope) matchesEntry(
func (ms mockScope) setDefaults() {} func (ms mockScope) setDefaults() {}
const ( const (
shouldMatch = "should-match-entry" shouldMatch = "should-match-entry"
stubResource = "stubResource"
) )
// helper funcs // helper funcs
@ -102,8 +101,6 @@ func stubScope(match string) mockScope {
return mockScope{ return mockScope{
rootCatStub.String(): passAny, rootCatStub.String(): passAny,
scopeKeyCategory: filters.Identity(rootCatStub.String()), scopeKeyCategory: filters.Identity(rootCatStub.String()),
scopeKeyGranularity: filters.Identity(Item),
scopeKeyResource: filters.Identity(stubResource),
scopeKeyDataType: filters.Identity(rootCatStub.String()), scopeKeyDataType: filters.Identity(rootCatStub.String()),
shouldMatch: filters.Identity(sm), shouldMatch: filters.Identity(sm),
} }
@ -113,15 +110,25 @@ func stubScope(match string) mockScope {
// selectors // selectors
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
func stubSelector() Selector { type mockSel struct {
return Selector{ Selector
Service: ServiceExchange, }
Excludes: []scope{scope(stubScope(""))},
Filters: []scope{scope(stubScope(""))}, func stubSelector() mockSel {
Includes: []scope{scope(stubScope(""))}, return mockSel{
Selector: Selector{
Service: ServiceExchange,
Excludes: []scope{scope(stubScope(""))},
Filters: []scope{scope(stubScope(""))},
Includes: []scope{scope(stubScope(""))},
},
} }
} }
func (s mockSel) Printable() Printable {
return toPrintable[mockScope](s.Selector)
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// helper funcs // helper funcs
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -46,6 +46,11 @@ func (s Selector) ToOneDriveBackup() (*OneDriveBackup, error) {
return &src, nil return &src, nil
} }
// Printable creates the minimized display of a selector, formatted for human readability.
func (s oneDrive) Printable() Printable {
return toPrintable[OneDriveScope](s.Selector)
}
// ------------------- // -------------------
// Scope Factories // Scope Factories
@ -113,7 +118,7 @@ func (s *oneDrive) Filter(scopes ...[]OneDriveScope) {
func (s *oneDrive) Users(users []string) []OneDriveScope { func (s *oneDrive) Users(users []string) []OneDriveScope {
scopes := []OneDriveScope{} scopes := []OneDriveScope{}
scopes = append(scopes, makeScope[OneDriveScope](Group, OneDriveUser, users, users)) scopes = append(scopes, makeScope[OneDriveScope](OneDriveUser, users, users))
return scopes return scopes
} }
@ -238,12 +243,6 @@ func (s OneDriveScope) FilterCategory() oneDriveCategory {
return oneDriveCategory(getFilterCategory(s)) return oneDriveCategory(getFilterCategory(s))
} }
// Granularity describes the granularity (directory || item)
// of the data in scope.
func (s OneDriveScope) Granularity() string {
return getGranularity(s)
}
// IncludeCategory checks whether the scope includes a // IncludeCategory checks whether the scope includes a
// certain category of data. // certain category of data.
// Ex: to check if the scope includes file data: // Ex: to check if the scope includes file data:

View File

@ -117,15 +117,12 @@ type (
// makeScope produces a well formatted, typed scope that ensures all base values are populated. // makeScope produces a well formatted, typed scope that ensures all base values are populated.
func makeScope[T scopeT]( func makeScope[T scopeT](
granularity string,
cat categorizer, cat categorizer,
resources, vs []string, resources, vs []string,
) T { ) T {
s := T{ s := T{
scopeKeyCategory: filters.Identity(cat.String()), scopeKeyCategory: filters.Identity(cat.String()),
scopeKeyDataType: filters.Identity(cat.leafCat().String()), scopeKeyDataType: filters.Identity(cat.leafCat().String()),
scopeKeyGranularity: filters.Identity(granularity),
scopeKeyResource: filters.Identity(join(resources...)),
cat.String(): filterize(vs...), cat.String(): filterize(vs...),
cat.rootCat().String(): filterize(resources...), cat.rootCat().String(): filterize(resources...),
} }
@ -141,12 +138,10 @@ func makeFilterScope[T scopeT](
f func([]string) filters.Filter, f func([]string) filters.Filter,
) T { ) T {
return T{ return T{
scopeKeyCategory: filters.Identity(cat.String()), scopeKeyCategory: filters.Identity(cat.String()),
scopeKeyDataType: filters.Identity(cat.leafCat().String()), scopeKeyDataType: filters.Identity(cat.leafCat().String()),
scopeKeyGranularity: filters.Identity(Filter), scopeKeyInfoFilter: filters.Identity(filterCat.String()),
scopeKeyInfoFilter: filters.Identity(filterCat.String()), filterCat.String(): f(clean(vs)),
scopeKeyResource: filters.Identity(Filter),
filterCat.String(): f(clean(vs)),
} }
} }
@ -179,11 +174,6 @@ func getFilterCategory[T scopeT](s T) string {
return s[scopeKeyInfoFilter].Target return s[scopeKeyInfoFilter].Target
} }
// getGranularity returns the scope's granularity value.
func getGranularity[T scopeT](s T) string {
return s[scopeKeyGranularity].Target
}
// getCatValue takes the value of s[cat], split it by the standard // getCatValue takes the value of s[cat], split it by the standard
// delimiter, and returns the slice. If s[cat] is nil, returns // delimiter, and returns the slice. If s[cat] is nil, returns
// None(). // None().
@ -203,12 +193,6 @@ func set[T scopeT](s T, cat categorizer, v []string) T {
return s return s
} }
// granularity describes the granularity (directory || item)
// of the data in scope.
func granularity[T scopeT](s T) string {
return s[scopeKeyGranularity].Target
}
// returns true if the category is included in the scope's category type, // returns true if the category is included in the scope's category type,
// and the value is set to Any(). // and the value is set to Any().
func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool { func isAnyTarget[T scopeT, C categoryT](s T, cat C) bool {

View File

@ -110,12 +110,6 @@ func (suite *SelectorScopesSuite) TestGetCatValue() {
assert.Equal(t, None(), getCatValue(stub, leafCatStub)) assert.Equal(t, None(), getCatValue(stub, leafCatStub))
} }
func (suite *SelectorScopesSuite) TestGranularity() {
t := suite.T()
stub := stubScope("")
assert.Equal(t, Item, granularity(stub))
}
func (suite *SelectorScopesSuite) TestIsAnyTarget() { func (suite *SelectorScopesSuite) TestIsAnyTarget() {
t := suite.T() t := suite.T()
stub := stubScope("") stub := stubScope("")
@ -125,13 +119,13 @@ func (suite *SelectorScopesSuite) TestIsAnyTarget() {
var reduceTestTable = []struct { var reduceTestTable = []struct {
name string name string
sel func() Selector sel func() mockSel
expectLen int expectLen int
expectPasses assert.BoolAssertionFunc expectPasses assert.BoolAssertionFunc
}{ }{
{ {
name: "include all", name: "include all",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Filters = nil sel.Filters = nil
sel.Excludes = nil sel.Excludes = nil
@ -142,7 +136,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "include none", name: "include none",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Includes[0] = scope(stubScope("none")) sel.Includes[0] = scope(stubScope("none"))
sel.Filters = nil sel.Filters = nil
@ -154,7 +148,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "filter and include all", name: "filter and include all",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Excludes = nil sel.Excludes = nil
return sel return sel
@ -164,7 +158,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "include all filter none", name: "include all filter none",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Filters[0] = scope(stubScope("none")) sel.Filters[0] = scope(stubScope("none"))
sel.Excludes = nil sel.Excludes = nil
@ -175,7 +169,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "include all exclude all", name: "include all exclude all",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Filters = nil sel.Filters = nil
return sel return sel
@ -185,7 +179,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "include all exclude none", name: "include all exclude none",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Filters = nil sel.Filters = nil
sel.Excludes[0] = scope(stubScope("none")) sel.Excludes[0] = scope(stubScope("none"))
@ -196,7 +190,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "filter all exclude all", name: "filter all exclude all",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Includes = nil sel.Includes = nil
return sel return sel
@ -206,7 +200,7 @@ var reduceTestTable = []struct {
}, },
{ {
name: "filter all exclude none", name: "filter all exclude none",
sel: func() Selector { sel: func() mockSel {
sel := stubSelector() sel := stubSelector()
sel.Includes = nil sel.Includes = nil
sel.Excludes[0] = scope(stubScope("none")) sel.Excludes[0] = scope(stubScope("none"))
@ -234,7 +228,7 @@ func (suite *SelectorScopesSuite) TestReduce() {
for _, test := range reduceTestTable { for _, test := range reduceTestTable {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
ds := deets() ds := deets()
result := reduce[mockScope](&ds, test.sel(), dataCats) result := reduce[mockScope](&ds, test.sel().Selector, dataCats)
require.NotNil(t, result) require.NotNil(t, result)
assert.Len(t, result.Entries, test.expectLen) assert.Len(t, result.Entries, test.expectLen)
}) })

View File

@ -22,20 +22,9 @@ const (
var ErrorBadSelectorCast = errors.New("wrong selector service type") var ErrorBadSelectorCast = errors.New("wrong selector service type")
const ( const (
scopeKeyCategory = "category" scopeKeyCategory = "category"
scopeKeyGranularity = "granularity" scopeKeyInfoFilter = "info_filter"
scopeKeyInfoFilter = "info_filter" scopeKeyDataType = "type"
scopeKeyResource = "resource"
scopeKeyDataType = "type"
)
// The granularity exprerssed by the scope. Groups imply non-item granularity,
// such as a directory. Items are individual files or objects.
// Filters are properties that search over service-specific info
const (
Group = "group"
Item = "item"
Filter = "filter"
) )
// The granularity exprerssed by the scope. Groups imply non-item granularity, // The granularity exprerssed by the scope. Groups imply non-item granularity,
@ -191,23 +180,43 @@ type Printable struct {
Includes map[string][]string `json:"includes,omitempty"` Includes map[string][]string `json:"includes,omitempty"`
} }
// Printable is the minimized display of a selector, formatted for human readability. // ToPrintable creates the minimized display of a selector, formatted for human readability.
// This transformer assumes that the scopeKeyResource and scopeKeyDataType have been func (s Selector) ToPrintable() Printable {
// added to all scopes as they were created. It is unable to infer resource or data switch s.Service {
// type values from existing scope values. case ServiceExchange:
func (s Selector) Printable() Printable { r, err := s.ToExchangeRestore()
if err != nil {
return Printable{}
}
return r.Printable()
case ServiceOneDrive:
r, err := s.ToOneDriveBackup()
if err != nil {
return Printable{}
}
return r.Printable()
}
return Printable{}
}
// toPrintable creates the minimized display of a selector, formatted for human readability.
func toPrintable[T scopeT](s Selector) Printable {
return Printable{ return Printable{
Service: s.Service.String(), Service: s.Service.String(),
Excludes: toResourceTypeMap(s.Excludes), Excludes: toResourceTypeMap[T](s.Excludes),
Filters: toResourceTypeMap(s.Filters), Filters: toResourceTypeMap[T](s.Filters),
Includes: toResourceTypeMap(s.Includes), Includes: toResourceTypeMap[T](s.Includes),
} }
} }
// Resources generates a tabular-readable output of the resources in Printable. // Resources generates a tabular-readable output of the resources in Printable.
// Only the first (arbitrarily picked) resource is displayed. All others are // Only the first (arbitrarily picked) resource is displayed. All others are
// simply counted. If no inclusions exist, uses Filters. If no filters exist, // simply counted. If no inclusions exist, uses Filters. If no filters exist,
// defaults to "All". // defaults to "None".
// Resource refers to the top-level entity in the service. User for Exchange, // Resource refers to the top-level entity in the service. User for Exchange,
// Site for sharepoint, etc. // Site for sharepoint, etc.
func (p Printable) Resources() string { func (p Printable) Resources() string {
@ -217,7 +226,7 @@ func (p Printable) Resources() string {
} }
if len(s) == 0 { if len(s) == 0 {
s = "All" s = "None"
} }
return s return s
@ -243,22 +252,23 @@ func resourcesShortFormat(m map[string][]string) string {
// Transforms the slice to a single map. // Transforms the slice to a single map.
// Keys are each map's scopeKeyResource value. // Keys are each map's scopeKeyResource value.
// Values are the set of all scopeKeyDataTypes for a given resource. // Values are the set of all scopeKeyDataTypes for a given resource.
func toResourceTypeMap(ms []scope) map[string][]string { func toResourceTypeMap[T scopeT](s []scope) map[string][]string {
if len(ms) == 0 { if len(s) == 0 {
return nil return nil
} }
r := make(map[string][]string) r := make(map[string][]string)
for _, m := range ms { for _, sc := range s {
res := m[scopeKeyResource] t := T(sc)
res := sc[t.categorizer().rootCat().String()]
k := res.Target k := res.Target
if res.Target == AnyTgt { if res.Target == AnyTgt {
k = All k = All
} }
r[k] = addToSet(r[k], split(m[scopeKeyDataType].Target)) r[k] = addToSet(r[k], split(sc[scopeKeyDataType].Target))
} }
return r return r

View File

@ -53,13 +53,21 @@ func (suite *SelectorSuite) TestPrintable_IncludedResources() {
p := sel.Printable() p := sel.Printable()
res := p.Resources() res := p.Resources()
assert.Equal(t, stubResource, res, "resource should state only the stub") assert.Equal(t, "All", res, "stub starts out as an all-pass")
stubWithResource := func(resource string) scope {
ss := stubScope("")
ss[rootCatStub.String()] = filterize(resource)
return scope(ss)
}
sel.Includes = []scope{ sel.Includes = []scope{
scope(stubScope("")), stubWithResource("foo"),
{scopeKeyResource: filterize("smarf"), scopeKeyDataType: filterize(unknownCatStub.String())}, stubWithResource("smarf"),
{scopeKeyResource: filterize("smurf"), scopeKeyDataType: filterize(unknownCatStub.String())}, stubWithResource("fnords"),
} }
p = sel.Printable() p = sel.Printable()
res = p.Resources() res = p.Resources()
@ -68,12 +76,12 @@ func (suite *SelectorSuite) TestPrintable_IncludedResources() {
p.Includes = nil p.Includes = nil
res = p.Resources() res = p.Resources()
assert.Equal(t, stubResource, res, "resource on filters should state only the stub") assert.Equal(t, "All", res, "filters is also an all-pass")
p.Filters = nil p.Filters = nil
res = p.Resources() res = p.Resources()
assert.Equal(t, "All", res, "resource with no Includes or Filters should state All") assert.Equal(t, "None", res, "resource with no Includes or Filters should state None")
} }
func (suite *SelectorSuite) TestToResourceTypeMap() { func (suite *SelectorSuite) TestToResourceTypeMap() {
@ -86,7 +94,7 @@ func (suite *SelectorSuite) TestToResourceTypeMap() {
name: "single scope", name: "single scope",
input: []scope{scope(stubScope(""))}, input: []scope{scope(stubScope(""))},
expect: map[string][]string{ expect: map[string][]string{
stubResource: {rootCatStub.String()}, "All": {rootCatStub.String()},
}, },
}, },
{ {
@ -94,13 +102,13 @@ func (suite *SelectorSuite) TestToResourceTypeMap() {
input: []scope{ input: []scope{
scope(stubScope("")), scope(stubScope("")),
{ {
scopeKeyResource: filterize("smarf"), rootCatStub.String(): filterize("smarf"),
scopeKeyDataType: filterize(unknownCatStub.String()), scopeKeyDataType: filterize(unknownCatStub.String()),
}, },
}, },
expect: map[string][]string{ expect: map[string][]string{
stubResource: {rootCatStub.String()}, "All": {rootCatStub.String()},
"smarf": {unknownCatStub.String()}, "smarf": {unknownCatStub.String()},
}, },
}, },
{ {
@ -108,18 +116,18 @@ func (suite *SelectorSuite) TestToResourceTypeMap() {
input: []scope{ input: []scope{
scope(stubScope("")), scope(stubScope("")),
{ {
scopeKeyResource: filterize(stubResource), rootCatStub.String(): filterize(AnyTgt),
scopeKeyDataType: filterize("other"), scopeKeyDataType: filterize("other"),
}, },
}, },
expect: map[string][]string{ expect: map[string][]string{
stubResource: {rootCatStub.String(), "other"}, "All": {rootCatStub.String(), "other"},
}, },
}, },
} }
for _, test := range table { for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) { suite.T().Run(test.name, func(t *testing.T) {
rtm := toResourceTypeMap(test.input) rtm := toResourceTypeMap[mockScope](test.input)
assert.Equal(t, test.expect, rtm) assert.Equal(t, test.expect, rtm)
}) })
} }