add info-based selector flags to backup details (#380)

* filter backup details by flags

`backup details` should have its output filtered by the flags provided by
the user.  In addition, the selector's FilterDetails should maintain
information (esp service info) about the entries, rather than slicing them
down to only the path reference.
This commit is contained in:
Keepers 2022-07-22 13:23:16 -06:00 committed by GitHub
parent 395c7c8525
commit 0739ea7e09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 257 additions and 93 deletions

View File

@ -20,15 +20,19 @@ import (
// exchange bucket info from flags // exchange bucket info from flags
var ( var (
backupID string backupID string
exchangeAll bool exchangeAll bool
exchangeData []string exchangeData []string
contact []string contact []string
contactFolder []string contactFolder []string
email []string email []string
emailFolder []string emailFolder []string
event []string emailReceivedAfter []string
user []string emailReceivedBefore []string
emailSender []string
emailSubject []string
event []string
user []string
) )
const ( const (
@ -66,6 +70,8 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
c, fs = utils.AddCommand(parent, exchangeDetailsCmd) c, fs = utils.AddCommand(parent, exchangeDetailsCmd)
fs.StringVar(&backupID, "backup", "", "ID of the backup containing the details to be shown") fs.StringVar(&backupID, "backup", "", "ID of the backup containing the details to be shown")
cobra.CheckErr(c.MarkFlagRequired("backup")) cobra.CheckErr(c.MarkFlagRequired("backup"))
// per-data-type flags
fs.StringArrayVar(&contact, "contact", nil, "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts") fs.StringArrayVar(&contact, "contact", nil, "Select backup details by contact ID; accepts "+utils.Wildcard+" to select all contacts")
fs.StringArrayVar( fs.StringArrayVar(
&contactFolder, &contactFolder,
@ -85,6 +91,12 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
cobra.CheckErr(fs.MarkHidden("contact")) cobra.CheckErr(fs.MarkHidden("contact"))
cobra.CheckErr(fs.MarkHidden("contact-folder")) cobra.CheckErr(fs.MarkHidden("contact-folder"))
cobra.CheckErr(fs.MarkHidden("event")) cobra.CheckErr(fs.MarkHidden("event"))
// exchange-info flags
fs.StringArrayVar(&emailReceivedAfter, "email-received-after", nil, "Select backup details where the email was received after this datetime")
fs.StringArrayVar(&emailReceivedBefore, "email-received-before", nil, "Select backup details where the email was received before this datetime")
fs.StringArrayVar(&emailSender, "email-sender", nil, "Select backup details where the email sender matches this user id")
fs.StringArrayVar(&emailSubject, "email-subject", nil, "Select backup details where the email subject lines contain this value")
} }
return c return c
@ -295,48 +307,60 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
return errors.Wrap(err, "Failed to get backup details in the repository") return errors.Wrap(err, "Failed to get backup details in the repository")
} }
sel := exchangeBackupDetailSelectors(contact, contactFolder, email, emailFolder, event, user) sel := selectors.NewExchangeRestore()
erSel, err := sel.ToExchangeRestore() includeExchangeBackupDetailDataSelectors(
if err != nil { sel,
return err contact,
contactFolder,
email,
emailFolder,
event,
user)
includeExchangeBackupDetailInfoSelectors(
sel,
emailReceivedAfter,
emailReceivedBefore,
emailSender,
emailSubject)
// if no selector flags were specified, get all data in the service.
if len(sel.Scopes()) == 0 {
sel.Include(sel.Users(selectors.Any()))
} }
ds := erSel.FilterDetails(d) ds := sel.FilterDetails(d)
print.Entries(ds.Entries) print.Entries(ds.Entries)
return nil return nil
} }
func exchangeBackupDetailSelectors( // builds the data-selector inclusions for `backup details exchange`
func includeExchangeBackupDetailDataSelectors(
sel *selectors.ExchangeRestore,
contacts, contactFolders, emails, emailFolders, events, users []string, contacts, contactFolders, emails, emailFolders, events, users []string,
) selectors.Selector { ) {
sel := selectors.NewExchangeBackup()
lc, lcf := len(contacts), len(contactFolders) lc, lcf := len(contacts), len(contactFolders)
le, lef := len(emails), len(emailFolders) le, lef := len(emails), len(emailFolders)
lev := len(events) lev := len(events)
lu := len(users) lu := len(users)
// if only the backupID is provided, treat that as an --all query
if lc+lcf+le+lef+lev+lu == 0 { if lc+lcf+le+lef+lev+lu == 0 {
sel.Include(sel.Users(selectors.Any())) return
return sel.Selector
} }
// if only users are provided, we only get one selector // if only users are provided, we only get one selector
if lc+lcf+le+lef+lev == 0 { if lu > 0 && lc+lcf+le+lef+lev == 0 {
sel.Include(sel.Users(users)) sel.Include(sel.Users(users))
return sel.Selector return
} }
// otherwise, add selectors for each type of data // otherwise, add selectors for each type of data
includeExchangeContacts(sel, users, contactFolders, contacts) includeExchangeContacts(sel, users, contactFolders, contacts)
includeExchangeEmails(sel, users, emailFolders, email) includeExchangeEmails(sel, users, emailFolders, email)
includeExchangeEvents(sel, users, events) includeExchangeEvents(sel, users, events)
return sel.Selector
} }
func includeExchangeContacts(sel *selectors.ExchangeBackup, users, contactFolders, contacts []string) { func includeExchangeContacts(sel *selectors.ExchangeRestore, users, contactFolders, contacts []string) {
if len(contactFolders) == 0 { if len(contactFolders) == 0 {
return return
} }
@ -347,7 +371,7 @@ func includeExchangeContacts(sel *selectors.ExchangeBackup, users, contactFolder
} }
} }
func includeExchangeEmails(sel *selectors.ExchangeBackup, users, emailFolders, emails []string) { func includeExchangeEmails(sel *selectors.ExchangeRestore, users, emailFolders, emails []string) {
if len(emailFolders) == 0 { if len(emailFolders) == 0 {
return return
} }
@ -358,13 +382,53 @@ func includeExchangeEmails(sel *selectors.ExchangeBackup, users, emailFolders, e
} }
} }
func includeExchangeEvents(sel *selectors.ExchangeBackup, users, events []string) { func includeExchangeEvents(sel *selectors.ExchangeRestore, users, events []string) {
if len(events) == 0 { if len(events) == 0 {
return return
} }
sel.Include(sel.Events(users, events)) sel.Include(sel.Events(users, events))
} }
// builds the info-selector inclusions for `backup details exchange`
func includeExchangeBackupDetailInfoSelectors(
sel *selectors.ExchangeRestore,
emailReceivedAfter, emailReceivedBefore, emailSender, emailSubject []string,
) {
includeExchangeInfoMailReceivedAfter(sel, emailReceivedAfter)
includeExchangeInfoMailReceivedBefore(sel, emailReceivedBefore)
includeExchangeInfoMailSender(sel, emailSender)
includeExchangeInfoMailSubject(sel, emailSubject)
}
func includeExchangeInfoMailReceivedAfter(sel *selectors.ExchangeRestore, receivedAfter []string) {
if len(receivedAfter) == 0 {
return
}
sel.Include(sel.MailReceivedAfter(receivedAfter))
}
func includeExchangeInfoMailReceivedBefore(sel *selectors.ExchangeRestore, receivedBefore []string) {
if len(receivedBefore) == 0 {
return
}
sel.Include(sel.MailReceivedBefore(receivedBefore))
}
func includeExchangeInfoMailSender(sel *selectors.ExchangeRestore, sender []string) {
if len(sender) == 0 {
return
}
sel.Include(sel.MailSender(sender))
}
func includeExchangeInfoMailSubject(sel *selectors.ExchangeRestore, subject []string) {
if len(subject) == 0 {
return
}
sel.Include(sel.MailSubject(subject))
}
// checks all flags for correctness and interdependencies
func validateExchangeBackupDetailFlags( func validateExchangeBackupDetailFlags(
contacts, contactFolders, emails, emailFolders, events, users []string, contacts, contactFolders, emails, emailFolders, events, users []string,
backupID string, backupID string,

View File

@ -10,6 +10,7 @@ import (
"github.com/alcionai/corso/cli/utils" "github.com/alcionai/corso/cli/utils"
ctesting "github.com/alcionai/corso/internal/testing" ctesting "github.com/alcionai/corso/internal/testing"
"github.com/alcionai/corso/pkg/selectors"
) )
type ExchangeSuite struct { type ExchangeSuite struct {
@ -54,17 +55,17 @@ func (suite *ExchangeSuite) TestAddExchangeCommands() {
func (suite *ExchangeSuite) TestValidateBackupCreateFlags() { func (suite *ExchangeSuite) TestValidateBackupCreateFlags() {
table := []struct { table := []struct {
name string name string
all bool any bool
user, data []string user, data []string
expect assert.ErrorAssertionFunc expect assert.ErrorAssertionFunc
}{ }{
{ {
name: "no users, not all", name: "no users, not any",
expect: assert.Error, expect: assert.Error,
}, },
{ {
name: "all and data", name: "any and data",
all: true, any: true,
data: []string{dataEmail}, data: []string{dataEmail},
expect: assert.Error, expect: assert.Error,
}, },
@ -75,25 +76,25 @@ func (suite *ExchangeSuite) TestValidateBackupCreateFlags() {
expect: assert.Error, expect: assert.Error,
}, },
{ {
name: "users, not all", name: "users, not any",
user: []string{"fnord"}, user: []string{"fnord"},
expect: assert.NoError, expect: assert.NoError,
}, },
{ {
name: "no users, all", name: "no users, any",
all: true, any: true,
expect: assert.NoError, expect: assert.NoError,
}, },
{ {
name: "users, all", name: "users, any",
all: true, any: true,
user: []string{"fnord"}, user: []string{"fnord"},
expect: assert.NoError, expect: assert.NoError,
}, },
} }
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) {
test.expect(t, validateExchangeBackupCreateFlags(test.all, test.user, test.data)) test.expect(t, validateExchangeBackupCreateFlags(test.any, test.user, test.data))
}) })
} }
} }
@ -101,17 +102,17 @@ func (suite *ExchangeSuite) TestValidateBackupCreateFlags() {
func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() { func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
table := []struct { table := []struct {
name string name string
all bool any bool
user, data []string user, data []string
expectIncludeLen int expectIncludeLen int
}{ }{
{ {
name: "all", name: "any",
all: true, any: true,
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, no data", name: "any users, no data",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
@ -121,7 +122,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "all users, contacts", name: "any users, contacts",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataContacts}, data: []string{dataContacts},
expectIncludeLen: 1, expectIncludeLen: 1,
@ -133,7 +134,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, email", name: "any users, email",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataEmail}, data: []string{dataEmail},
expectIncludeLen: 1, expectIncludeLen: 1,
@ -145,7 +146,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, events", name: "any users, events",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataEvents}, data: []string{dataEvents},
expectIncludeLen: 1, expectIncludeLen: 1,
@ -157,7 +158,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, contacts + email", name: "any users, contacts + email",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataContacts, dataEmail}, data: []string{dataContacts, dataEmail},
expectIncludeLen: 2, expectIncludeLen: 2,
@ -169,7 +170,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "all users, email + events", name: "any users, email + events",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataEmail, dataEvents}, data: []string{dataEmail, dataEvents},
expectIncludeLen: 2, expectIncludeLen: 2,
@ -181,7 +182,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "all users, events + contacts", name: "any users, events + contacts",
user: []string{utils.Wildcard}, user: []string{utils.Wildcard},
data: []string{dataEvents, dataContacts}, data: []string{dataEvents, dataContacts},
expectIncludeLen: 2, expectIncludeLen: 2,
@ -207,7 +208,7 @@ func (suite *ExchangeSuite) TestExchangeBackupCreateSelectors() {
} }
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) {
sel := exchangeBackupCreateSelectors(test.all, test.user, test.data) sel := exchangeBackupCreateSelectors(test.any, test.user, test.data)
assert.Equal(t, test.expectIncludeLen, len(sel.Includes)) assert.Equal(t, test.expectIncludeLen, len(sel.Includes))
}) })
} }
@ -227,7 +228,7 @@ func (suite *ExchangeSuite) TestValidateBackupDetailFlags() {
expect: assert.NoError, expect: assert.NoError,
}, },
{ {
name: "all values populated", name: "any values populated",
backupID: "bid", backupID: "bid",
contacts: stub, contacts: stub,
contactFolders: stub, contactFolders: stub,
@ -297,9 +298,9 @@ func (suite *ExchangeSuite) TestValidateBackupDetailFlags() {
} }
} }
func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() { func (suite *ExchangeSuite) TestIncludeExchangeBackupDetailDataSelectors() {
stub := []string{"id-stub"} stub := []string{"id-stub"}
all := []string{utils.Wildcard} any := []string{utils.Wildcard}
table := []struct { table := []struct {
name string name string
contacts, contactFolders, emails, emailFolders, events, users []string contacts, contactFolders, emails, emailFolders, events, users []string
@ -307,11 +308,11 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
}{ }{
{ {
name: "no selectors", name: "no selectors",
expectIncludeLen: 1, expectIncludeLen: 0,
}, },
{ {
name: "all users", name: "any users",
users: all, users: any,
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
@ -325,20 +326,20 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, all data", name: "any users, any data",
contacts: all, contacts: any,
contactFolders: all, contactFolders: any,
emails: all, emails: any,
emailFolders: all, emailFolders: any,
events: all, events: any,
users: all, users: any,
expectIncludeLen: 3, expectIncludeLen: 3,
}, },
{ {
name: "all users, all folders", name: "any users, any folders",
contactFolders: all, contactFolders: any,
emailFolders: all, emailFolders: any,
users: all, users: any,
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
@ -359,10 +360,10 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "all users, contacts", name: "any users, contacts",
contacts: all, contacts: any,
contactFolders: stub, contactFolders: stub,
users: all, users: any,
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
@ -373,10 +374,10 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, emails", name: "any users, emails",
emails: all, emails: any,
emailFolders: stub, emailFolders: stub,
users: all, users: any,
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
@ -387,9 +388,9 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, events", name: "any users, events",
events: all, events: any,
users: all, users: any,
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
@ -399,12 +400,12 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 1, expectIncludeLen: 1,
}, },
{ {
name: "all users, contacts + email", name: "any users, contacts + email",
contacts: all, contacts: any,
contactFolders: all, contactFolders: any,
emails: all, emails: any,
emailFolders: all, emailFolders: any,
users: all, users: any,
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
@ -417,11 +418,11 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "all users, email + event", name: "any users, email + event",
emails: all, emails: any,
emailFolders: all, emailFolders: any,
events: all, events: any,
users: all, users: any,
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
@ -433,11 +434,11 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
name: "all users, event + contact", name: "any users, event + contact",
contacts: all, contacts: any,
contactFolders: all, contactFolders: any,
events: all, events: any,
users: all, users: any,
expectIncludeLen: 2, expectIncludeLen: 2,
}, },
{ {
@ -465,7 +466,9 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
} }
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) {
sel := exchangeBackupDetailSelectors( sel := selectors.NewExchangeRestore()
includeExchangeBackupDetailDataSelectors(
sel,
test.contacts, test.contacts,
test.contactFolders, test.contactFolders,
test.emails, test.emails,
@ -476,3 +479,99 @@ func (suite *ExchangeSuite) TestExchangeBackupDetailSelectors() {
}) })
} }
} }
func (suite *ExchangeSuite) TestIncludeExchangeBackupDetailInfoSelectors() {
stub := []string{"id-stub"}
twoStubs := []string{"smarfs", "fnords"}
any := []string{utils.Wildcard}
table := []struct {
name string
after, before, sender, subject []string
expectIncludeLen int
}{
{
name: "no selectors",
expectIncludeLen: 0,
},
{
name: "any receivedAfter",
after: any,
expectIncludeLen: 1,
},
{
name: "single receivedAfter",
after: stub,
expectIncludeLen: 1,
},
{
name: "multiple receivedAfter",
after: twoStubs,
expectIncludeLen: 1,
},
{
name: "any receivedBefore",
before: any,
expectIncludeLen: 1,
},
{
name: "single receivedBefore",
before: stub,
expectIncludeLen: 1,
},
{
name: "multiple receivedBefore",
before: twoStubs,
expectIncludeLen: 1,
},
{
name: "any sender",
sender: any,
expectIncludeLen: 1,
},
{
name: "single sender",
sender: stub,
expectIncludeLen: 1,
},
{
name: "multiple senders",
sender: twoStubs,
expectIncludeLen: 1,
},
{
name: "any subject",
subject: any,
expectIncludeLen: 1,
},
{
name: "single subject",
subject: stub,
expectIncludeLen: 1,
},
{
name: "multiple subjects",
subject: twoStubs,
expectIncludeLen: 1,
},
{
name: "one of each",
after: stub,
before: stub,
sender: stub,
subject: stub,
expectIncludeLen: 4,
},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
sel := selectors.NewExchangeRestore()
includeExchangeBackupDetailInfoSelectors(
sel,
test.after,
test.before,
test.sender,
test.subject)
assert.Equal(t, test.expectIncludeLen, len(sel.Includes))
})
}
}

View File

@ -4,10 +4,11 @@ import (
"testing" "testing"
"time" "time"
"github.com/alcionai/corso/internal/common"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
"github.com/alcionai/corso/internal/common"
) )
type CommonTimeUnitSuite struct { type CommonTimeUnitSuite struct {

View File

@ -77,7 +77,7 @@ type DetailsEntry struct {
ItemInfo ItemInfo
} }
// Paths returns the list of Paths extracted from the Entriess slice. // Paths returns the list of Paths extracted from the Entries slice.
func (dm DetailsModel) Paths() []string { func (dm DetailsModel) Paths() []string {
ents := dm.Entries ents := dm.Entries
r := make([]string, len(ents)) r := make([]string, len(ents))