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
var (
backupID string
exchangeAll bool
exchangeData []string
contact []string
contactFolder []string
email []string
emailFolder []string
event []string
user []string
backupID string
exchangeAll bool
exchangeData []string
contact []string
contactFolder []string
email []string
emailFolder []string
emailReceivedAfter []string
emailReceivedBefore []string
emailSender []string
emailSubject []string
event []string
user []string
)
const (
@ -66,6 +70,8 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
c, fs = utils.AddCommand(parent, exchangeDetailsCmd)
fs.StringVar(&backupID, "backup", "", "ID of the backup containing the details to be shown")
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(
&contactFolder,
@ -85,6 +91,12 @@ func addExchangeCommands(parent *cobra.Command) *cobra.Command {
cobra.CheckErr(fs.MarkHidden("contact"))
cobra.CheckErr(fs.MarkHidden("contact-folder"))
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
@ -295,48 +307,60 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
return errors.Wrap(err, "Failed to get backup details in the repository")
}
sel := exchangeBackupDetailSelectors(contact, contactFolder, email, emailFolder, event, user)
erSel, err := sel.ToExchangeRestore()
if err != nil {
return err
sel := selectors.NewExchangeRestore()
includeExchangeBackupDetailDataSelectors(
sel,
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)
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,
) selectors.Selector {
sel := selectors.NewExchangeBackup()
) {
lc, lcf := len(contacts), len(contactFolders)
le, lef := len(emails), len(emailFolders)
lev := len(events)
lu := len(users)
// if only the backupID is provided, treat that as an --all query
if lc+lcf+le+lef+lev+lu == 0 {
sel.Include(sel.Users(selectors.Any()))
return sel.Selector
return
}
// 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))
return sel.Selector
return
}
// otherwise, add selectors for each type of data
includeExchangeContacts(sel, users, contactFolders, contacts)
includeExchangeEmails(sel, users, emailFolders, email)
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 {
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 {
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 {
return
}
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(
contacts, contactFolders, emails, emailFolders, events, users []string,
backupID string,

View File

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

View File

@ -77,7 +77,7 @@ type DetailsEntry struct {
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 {
ents := dm.Entries
r := make([]string, len(ents))