introduce id-name lookup maps
Adds two maps to resource-owner handling: id-to-name and name-to-id. Expectation is that these maps will either get populated by a caller as a pre-process before initializing the gc client, or gc will (later pr) be able to look up the owner and populate those maps itself. The maps are used to set the selector id and name for iface compliance. Only supported by exchange in this PR.
This commit is contained in:
parent
547b8c767e
commit
cdbf3910e8
@ -195,6 +195,8 @@ func runBackups(
|
|||||||
r repository.Repository,
|
r repository.Repository,
|
||||||
serviceName, resourceOwnerType string,
|
serviceName, resourceOwnerType string,
|
||||||
selectorSet []selectors.Selector,
|
selectorSet []selectors.Selector,
|
||||||
|
resourceOwnersIDToName map[string]string,
|
||||||
|
resourceOwnersNameToID map[string]string,
|
||||||
) error {
|
) error {
|
||||||
var (
|
var (
|
||||||
bIDs []model.StableID
|
bIDs []model.StableID
|
||||||
@ -204,21 +206,21 @@ func runBackups(
|
|||||||
for _, discSel := range selectorSet {
|
for _, discSel := range selectorSet {
|
||||||
var (
|
var (
|
||||||
owner = discSel.DiscreteOwner
|
owner = discSel.DiscreteOwner
|
||||||
bctx = clues.Add(ctx, "resource_owner", owner)
|
ictx = clues.Add(ctx, "resource_owner", owner)
|
||||||
)
|
)
|
||||||
|
|
||||||
bo, err := r.NewBackup(bctx, discSel)
|
bo, err := r.NewBackup(ictx, discSel, resourceOwnersIDToName, resourceOwnersNameToID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, clues.Wrap(err, owner).WithClues(bctx))
|
errs = append(errs, clues.Wrap(err, owner).WithClues(ictx))
|
||||||
Errf(bctx, "%v\n", err)
|
Errf(ictx, "%v\n", err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err = bo.Run(bctx)
|
err = bo.Run(ictx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, clues.Wrap(err, owner).WithClues(bctx))
|
errs = append(errs, clues.Wrap(err, owner).WithClues(ictx))
|
||||||
Errf(bctx, "%v\n", err)
|
Errf(ictx, "%v\n", err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cli/options"
|
"github.com/alcionai/corso/src/cli/options"
|
||||||
. "github.com/alcionai/corso/src/cli/print"
|
. "github.com/alcionai/corso/src/cli/print"
|
||||||
@ -164,14 +165,14 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
|||||||
// TODO: log/print recoverable errors
|
// TODO: log/print recoverable errors
|
||||||
errs := fault.New(false)
|
errs := fault.New(false)
|
||||||
|
|
||||||
users, err := m365.UserPNs(ctx, *acct, errs)
|
idToPN, pnToID, err := m365.UsersMap(ctx, *acct, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 user(s)"))
|
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 user(s)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
selectorSet := []selectors.Selector{}
|
selectorSet := []selectors.Selector{}
|
||||||
|
|
||||||
for _, discSel := range sel.SplitByResourceOwner(users) {
|
for _, discSel := range sel.SplitByResourceOwner(maps.Keys(pnToID)) {
|
||||||
selectorSet = append(selectorSet, discSel.Selector)
|
selectorSet = append(selectorSet, discSel.Selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +181,7 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
|||||||
r,
|
r,
|
||||||
"Exchange", "user",
|
"Exchange", "user",
|
||||||
selectorSet,
|
selectorSet,
|
||||||
)
|
idToPN, pnToID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exchangeBackupCreateSelectors(userIDs, cats []string) *selectors.ExchangeBackup {
|
func exchangeBackupCreateSelectors(userIDs, cats []string) *selectors.ExchangeBackup {
|
||||||
|
|||||||
@ -300,7 +300,11 @@ func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
|
|||||||
|
|
||||||
suite.backupOps = make(map[path.CategoryType]string)
|
suite.backupOps = make(map[path.CategoryType]string)
|
||||||
|
|
||||||
users := []string{suite.m365UserID}
|
var (
|
||||||
|
users = []string{suite.m365UserID}
|
||||||
|
idToName = map[string]string{suite.m365UserID: "todo-name-" + suite.m365UserID}
|
||||||
|
nameToID = map[string]string{"todo-name-" + suite.m365UserID: suite.m365UserID}
|
||||||
|
)
|
||||||
|
|
||||||
for _, set := range backupDataSets {
|
for _, set := range backupDataSets {
|
||||||
var (
|
var (
|
||||||
@ -321,7 +325,7 @@ func (suite *PreparedBackupExchangeE2ESuite) SetupSuite() {
|
|||||||
|
|
||||||
sel.Include(scopes)
|
sel.Include(scopes)
|
||||||
|
|
||||||
bop, err := suite.repo.NewBackup(ctx, sel.Selector)
|
bop, err := suite.repo.NewBackup(ctx, sel.Selector, idToName, nameToID)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = bop.Run(ctx)
|
err = bop.Run(ctx)
|
||||||
@ -546,7 +550,7 @@ func (suite *BackupDeleteExchangeE2ESuite) SetupSuite() {
|
|||||||
sel := selectors.NewExchangeBackup(users)
|
sel := selectors.NewExchangeBackup(users)
|
||||||
sel.Include(sel.MailFolders([]string{exchange.DefaultMailFolder}, selectors.PrefixMatch()))
|
sel.Include(sel.MailFolders([]string{exchange.DefaultMailFolder}, selectors.PrefixMatch()))
|
||||||
|
|
||||||
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector)
|
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector, nil, nil)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = suite.backupOp.Run(ctx)
|
err = suite.backupOp.Run(ctx)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cli/options"
|
"github.com/alcionai/corso/src/cli/options"
|
||||||
. "github.com/alcionai/corso/src/cli/print"
|
. "github.com/alcionai/corso/src/cli/print"
|
||||||
@ -148,14 +149,14 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error {
|
|||||||
// TODO: log/print recoverable errors
|
// TODO: log/print recoverable errors
|
||||||
errs := fault.New(false)
|
errs := fault.New(false)
|
||||||
|
|
||||||
users, err := m365.UserPNs(ctx, *acct, errs)
|
idToName, nameToID, err := m365.UsersMap(ctx, *acct, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 users"))
|
return Only(ctx, clues.Wrap(err, "Failed to retrieve M365 users"))
|
||||||
}
|
}
|
||||||
|
|
||||||
selectorSet := []selectors.Selector{}
|
selectorSet := []selectors.Selector{}
|
||||||
|
|
||||||
for _, discSel := range sel.SplitByResourceOwner(users) {
|
for _, discSel := range sel.SplitByResourceOwner(maps.Keys(idToName)) {
|
||||||
selectorSet = append(selectorSet, discSel.Selector)
|
selectorSet = append(selectorSet, discSel.Selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error {
|
|||||||
r,
|
r,
|
||||||
"OneDrive", "user",
|
"OneDrive", "user",
|
||||||
selectorSet,
|
selectorSet,
|
||||||
)
|
idToName, nameToID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOneDriveBackupCreateFlags(users []string) error {
|
func validateOneDriveBackupCreateFlags(users []string) error {
|
||||||
|
|||||||
@ -205,14 +205,18 @@ func (suite *BackupDeleteOneDriveE2ESuite) SetupSuite() {
|
|||||||
})
|
})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
m365UserID := tester.M365UserID(t)
|
var (
|
||||||
users := []string{m365UserID}
|
m365UserID = tester.M365UserID(t)
|
||||||
|
users = []string{m365UserID}
|
||||||
|
idToName = map[string]string{m365UserID: "todo-name-" + m365UserID}
|
||||||
|
nameToID = map[string]string{"todo-name-" + m365UserID: m365UserID}
|
||||||
|
)
|
||||||
|
|
||||||
// some tests require an existing backup
|
// some tests require an existing backup
|
||||||
sel := selectors.NewOneDriveBackup(users)
|
sel := selectors.NewOneDriveBackup(users)
|
||||||
sel.Include(sel.Folders(selectors.Any()))
|
sel.Include(sel.Folders(selectors.Any()))
|
||||||
|
|
||||||
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector)
|
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector, idToName, nameToID)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = suite.backupOp.Run(ctx)
|
err = suite.backupOp.Run(ctx)
|
||||||
|
|||||||
@ -154,6 +154,7 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error {
|
|||||||
// TODO: log/print recoverable errors
|
// TODO: log/print recoverable errors
|
||||||
errs := fault.New(false)
|
errs := fault.New(false)
|
||||||
|
|
||||||
|
// TODO: discovery of sharepoint sites instead of early GC construction.
|
||||||
gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), *acct, connector.Sites, errs)
|
gc, err := connector.NewGraphConnector(ctx, graph.HTTPClient(graph.NoTimeout()), *acct, connector.Sites, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Only(ctx, clues.Wrap(err, "Failed to connect to Microsoft APIs"))
|
return Only(ctx, clues.Wrap(err, "Failed to connect to Microsoft APIs"))
|
||||||
@ -175,7 +176,7 @@ func createSharePointCmd(cmd *cobra.Command, args []string) error {
|
|||||||
r,
|
r,
|
||||||
"SharePoint", "site",
|
"SharePoint", "site",
|
||||||
selectorSet,
|
selectorSet,
|
||||||
)
|
nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSharePointBackupCreateFlags(sites, weburls, cats []string) error {
|
func validateSharePointBackupCreateFlags(sites, weburls, cats []string) error {
|
||||||
|
|||||||
@ -156,14 +156,18 @@ func (suite *BackupDeleteSharePointE2ESuite) SetupSuite() {
|
|||||||
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
m365SiteID := tester.M365SiteID(t)
|
var (
|
||||||
sites := []string{m365SiteID}
|
m365SiteID = tester.M365SiteID(t)
|
||||||
|
sites = []string{m365SiteID}
|
||||||
|
idToName = map[string]string{m365SiteID: "todo-name-" + m365SiteID}
|
||||||
|
nameToID = map[string]string{"todo-name-" + m365SiteID: m365SiteID}
|
||||||
|
)
|
||||||
|
|
||||||
// some tests require an existing backup
|
// some tests require an existing backup
|
||||||
sel := selectors.NewSharePointBackup(sites)
|
sel := selectors.NewSharePointBackup(sites)
|
||||||
sel.Include(sel.LibraryFolders(selectors.Any()))
|
sel.Include(sel.LibraryFolders(selectors.Any()))
|
||||||
|
|
||||||
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector)
|
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector, idToName, nameToID)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = suite.backupOp.Run(ctx)
|
err = suite.backupOp.Run(ctx)
|
||||||
|
|||||||
@ -73,7 +73,12 @@ func (suite *RestoreExchangeE2ESuite) SetupSuite() {
|
|||||||
suite.vpr, suite.cfgFP = tester.MakeTempTestConfigClone(t, force)
|
suite.vpr, suite.cfgFP = tester.MakeTempTestConfigClone(t, force)
|
||||||
|
|
||||||
suite.m365UserID = tester.M365UserID(t)
|
suite.m365UserID = tester.M365UserID(t)
|
||||||
users := []string{suite.m365UserID}
|
|
||||||
|
var (
|
||||||
|
users = []string{suite.m365UserID}
|
||||||
|
idToName = map[string]string{suite.m365UserID: "todo-name-" + suite.m365UserID}
|
||||||
|
nameToID = map[string]string{"todo-name-" + suite.m365UserID: suite.m365UserID}
|
||||||
|
)
|
||||||
|
|
||||||
// init the repo first
|
// init the repo first
|
||||||
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{})
|
||||||
@ -100,7 +105,7 @@ func (suite *RestoreExchangeE2ESuite) SetupSuite() {
|
|||||||
|
|
||||||
sel.Include(scopes)
|
sel.Include(scopes)
|
||||||
|
|
||||||
bop, err := suite.repo.NewBackup(ctx, sel.Selector)
|
bop, err := suite.repo.NewBackup(ctx, sel.Selector, idToName, nameToID)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = bop.Run(ctx)
|
err = bop.Run(ctx)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
"github.com/alcionai/corso/src/cli/print"
|
"github.com/alcionai/corso/src/cli/print"
|
||||||
"github.com/alcionai/corso/src/internal/common"
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
@ -126,12 +127,12 @@ func getGCAndVerifyUser(ctx context.Context, userID string) (*connector.GraphCon
|
|||||||
errs := fault.New(false)
|
errs := fault.New(false)
|
||||||
normUsers := map[string]struct{}{}
|
normUsers := map[string]struct{}{}
|
||||||
|
|
||||||
users, err := m365.UserPNs(ctx, acct, errs)
|
idToName, _, err := m365.UsersMap(ctx, acct, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, account.Account{}, clues.Wrap(err, "getting tenant users")
|
return nil, account.Account{}, clues.Wrap(err, "getting tenant users")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, k := range users {
|
for _, k := range maps.Keys(idToName) {
|
||||||
normUsers[strings.ToLower(k)] = struct{}{}
|
normUsers[strings.ToLower(k)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,9 +42,17 @@ type GraphConnector struct {
|
|||||||
itemClient *http.Client // configured to handle large item downloads
|
itemClient *http.Client // configured to handle large item downloads
|
||||||
|
|
||||||
tenant string
|
tenant string
|
||||||
Sites map[string]string // webURL -> siteID and siteID -> webURL
|
|
||||||
credentials account.M365Config
|
credentials account.M365Config
|
||||||
|
|
||||||
|
// TODO: remove in favor of the maps below.
|
||||||
|
Sites map[string]string // webURL -> siteID and siteID -> webURL
|
||||||
|
|
||||||
|
// maps of resource owner ids to names, and names to ids.
|
||||||
|
// not guaranteed to be populated, only here as a post-population
|
||||||
|
// reference for processes that choose to populate the values.
|
||||||
|
ResourceOwnerIDToName map[string]string
|
||||||
|
ResourceOwnerNameToID map[string]string
|
||||||
|
|
||||||
// wg is used to track completion of GC tasks
|
// wg is used to track completion of GC tasks
|
||||||
wg *sync.WaitGroup
|
wg *sync.WaitGroup
|
||||||
region *trace.Region
|
region *trace.Region
|
||||||
@ -101,6 +109,63 @@ func NewGraphConnector(
|
|||||||
return &gc, nil
|
return &gc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PopulateOwnerIDAndNamesFrom takes the provided owner identifier and produces
|
||||||
|
// the owner's name and ID from that value. Returns an error if the owner is
|
||||||
|
// not recognized by the current tenant.
|
||||||
|
//
|
||||||
|
// The id-name maps are optional. Some processes will look up all owners in
|
||||||
|
// the tenant before reaching this step. In that case, the data gets handed
|
||||||
|
// down for this func to consume instead of performing further queries. The
|
||||||
|
// maps get stored inside the gc instance for later re-use.
|
||||||
|
//
|
||||||
|
// TODO: If the maps are nil or empty, this func will perform a lookup on the given
|
||||||
|
// owner, and populate each map with that owner's id and name for downstream
|
||||||
|
// guarantees about that data being present. Optional performance enhancement
|
||||||
|
// idea: downstream from here, we should _only_ need the given user's id and name,
|
||||||
|
// and could store minimal map copies with that info instead of the whole tenant.
|
||||||
|
func (gc *GraphConnector) PopulateOwnerIDAndNamesFrom(
|
||||||
|
owner string, // input value, can be either id or name
|
||||||
|
idToName, nameToID map[string]string, // optionally pre-populated lookups
|
||||||
|
) (string, string, error) {
|
||||||
|
// ensure the maps exist, even if they aren't populated so that
|
||||||
|
// getOwnerIDAndNameFrom can populate any values it looks up.
|
||||||
|
if len(idToName) == 0 {
|
||||||
|
idToName = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nameToID) == 0 {
|
||||||
|
nameToID = map[string]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// move this to GC method
|
||||||
|
id, name, err := getOwnerIDAndNameFrom(owner, idToName, nameToID)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.Wrap(err, "resolving resource owner details")
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.ResourceOwnerIDToName = idToName
|
||||||
|
gc.ResourceOwnerNameToID = nameToID
|
||||||
|
|
||||||
|
return id, name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOwnerIDAndNameFrom(
|
||||||
|
owner string,
|
||||||
|
idToName, nameToID map[string]string,
|
||||||
|
) (string, string, error) {
|
||||||
|
if n, ok := idToName[owner]; ok {
|
||||||
|
return owner, n, nil
|
||||||
|
} else if i, ok := nameToID[owner]; ok {
|
||||||
|
return i, owner, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: look-up user by owner, either id or name,
|
||||||
|
// and populate with maps as a result. Only
|
||||||
|
// return owner, owner as a very last resort.
|
||||||
|
|
||||||
|
return owner, owner, nil
|
||||||
|
}
|
||||||
|
|
||||||
// createService constructor for graphService component
|
// createService constructor for graphService component
|
||||||
func (gc *GraphConnector) createService() (*graph.Service, error) {
|
func (gc *GraphConnector) createService() (*graph.Service, error) {
|
||||||
adapter, err := graph.CreateAdapter(
|
adapter, err := graph.CreateAdapter(
|
||||||
|
|||||||
@ -123,11 +123,11 @@ func (suite *GraphConnectorUnitSuite) TestUnionSiteIDsAndWebURLs() {
|
|||||||
}
|
}
|
||||||
for _, test := range table {
|
for _, test := range table {
|
||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
t := suite.T()
|
|
||||||
|
|
||||||
ctx, flush := tester.NewContext()
|
ctx, flush := tester.NewContext()
|
||||||
defer flush()
|
defer flush()
|
||||||
|
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
result, err := gc.UnionSiteIDsAndWebURLs(ctx, test.ids, test.urls, fault.New(true))
|
result, err := gc.UnionSiteIDsAndWebURLs(ctx, test.ids, test.urls, fault.New(true))
|
||||||
assert.NoError(t, err, clues.ToCore(err))
|
assert.NoError(t, err, clues.ToCore(err))
|
||||||
assert.ElementsMatch(t, test.expect, result)
|
assert.ElementsMatch(t, test.expect, result)
|
||||||
@ -135,6 +135,113 @@ func (suite *GraphConnectorUnitSuite) TestUnionSiteIDsAndWebURLs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *GraphConnectorUnitSuite) TestPopulateOwnerIDAndNamesFrom() {
|
||||||
|
const (
|
||||||
|
ownerID = "owner-id"
|
||||||
|
ownerName = "owner-name"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
itn = map[string]string{ownerID: ownerName}
|
||||||
|
nti = map[string]string{ownerName: ownerID}
|
||||||
|
)
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
owner string
|
||||||
|
idToName map[string]string
|
||||||
|
nameToID map[string]string
|
||||||
|
expectID string
|
||||||
|
expectName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil maps",
|
||||||
|
owner: ownerID,
|
||||||
|
idToName: nil,
|
||||||
|
nameToID: nil,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only id map with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
idToName: itn,
|
||||||
|
nameToID: nil,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only name map with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
idToName: nil,
|
||||||
|
nameToID: nti,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only id map with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
idToName: itn,
|
||||||
|
nameToID: nil,
|
||||||
|
expectID: ownerName,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only name map with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
idToName: nil,
|
||||||
|
nameToID: nti,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both maps with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
idToName: itn,
|
||||||
|
nameToID: nti,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both maps with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
idToName: itn,
|
||||||
|
nameToID: nti,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching maps with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
idToName: map[string]string{"foo": "bar"},
|
||||||
|
nameToID: map[string]string{"fnords": "smarf"},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
idToName: map[string]string{"foo": "bar"},
|
||||||
|
nameToID: map[string]string{"fnords": "smarf"},
|
||||||
|
expectID: ownerName,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
gc = &GraphConnector{}
|
||||||
|
)
|
||||||
|
|
||||||
|
id, name, err := gc.PopulateOwnerIDAndNamesFrom(test.owner, test.idToName, test.nameToID)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
assert.Equal(t, test.expectID, id)
|
||||||
|
assert.Equal(t, test.expectName, name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *GraphConnectorUnitSuite) TestGraphConnector_Wait() {
|
func (suite *GraphConnectorUnitSuite) TestGraphConnector_Wait() {
|
||||||
ctx, flush := tester.NewContext()
|
ctx, flush := tester.NewContext()
|
||||||
defer flush()
|
defer flush()
|
||||||
|
|||||||
@ -120,7 +120,7 @@ func runLoadTest(
|
|||||||
) {
|
) {
|
||||||
//revive:enable:context-as-argument
|
//revive:enable:context-as-argument
|
||||||
t.Run(prefix+"_load_test_main", func(t *testing.T) {
|
t.Run(prefix+"_load_test_main", func(t *testing.T) {
|
||||||
b, err := r.NewBackup(ctx, bupSel)
|
b, err := r.NewBackup(ctx, bupSel, nil, nil)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
runBackupLoadTest(t, ctx, &b, service, usersUnderTest)
|
runBackupLoadTest(t, ctx, &b, service, usersUnderTest)
|
||||||
@ -447,8 +447,7 @@ func (suite *LoadExchangeSuite) TestExchange() {
|
|||||||
"all_users", "exchange",
|
"all_users", "exchange",
|
||||||
suite.usersUnderTest,
|
suite.usersUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
true,
|
true)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// single user, lots of data
|
// single user, lots of data
|
||||||
@ -500,8 +499,7 @@ func (suite *IndividualLoadExchangeSuite) TestExchange() {
|
|||||||
"single_user", "exchange",
|
"single_user", "exchange",
|
||||||
suite.usersUnderTest,
|
suite.usersUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
true,
|
true)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
@ -553,8 +551,7 @@ func (suite *LoadOneDriveSuite) TestOneDrive() {
|
|||||||
"all_users", "one_drive",
|
"all_users", "one_drive",
|
||||||
suite.usersUnderTest,
|
suite.usersUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
false,
|
false)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IndividualLoadOneDriveSuite struct {
|
type IndividualLoadOneDriveSuite struct {
|
||||||
@ -601,8 +598,7 @@ func (suite *IndividualLoadOneDriveSuite) TestOneDrive() {
|
|||||||
"single_user", "one_drive",
|
"single_user", "one_drive",
|
||||||
suite.usersUnderTest,
|
suite.usersUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
false,
|
false)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
@ -654,8 +650,7 @@ func (suite *LoadSharePointSuite) TestSharePoint() {
|
|||||||
"all_sites", "share_point",
|
"all_sites", "share_point",
|
||||||
suite.sitesUnderTest,
|
suite.sitesUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
false,
|
false)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IndividualLoadSharePointSuite struct {
|
type IndividualLoadSharePointSuite struct {
|
||||||
@ -703,6 +698,5 @@ func (suite *IndividualLoadSharePointSuite) TestSharePoint() {
|
|||||||
"single_site", "share_point",
|
"single_site", "share_point",
|
||||||
suite.sitesUnderTest,
|
suite.sitesUnderTest,
|
||||||
sel, sel, // same selection for backup and restore
|
sel, sel, // same selection for backup and restore
|
||||||
false,
|
false)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,6 +54,7 @@ type Repository interface {
|
|||||||
NewBackup(
|
NewBackup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
self selectors.Selector,
|
self selectors.Selector,
|
||||||
|
ownerIDToName, ownerNameToID map[string]string,
|
||||||
) (operations.BackupOperation, error)
|
) (operations.BackupOperation, error)
|
||||||
NewRestore(
|
NewRestore(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -283,17 +284,25 @@ func (r *repository) Close(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBackup generates a BackupOperation runner.
|
// NewBackup generates a BackupOperation runner.
|
||||||
|
// ownerIDToName and ownerNameToID are optional populations, in case the caller has
|
||||||
|
// already generated those values.
|
||||||
func (r repository) NewBackup(
|
func (r repository) NewBackup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
sel selectors.Selector,
|
sel selectors.Selector,
|
||||||
|
ownerIDToName, ownerNameToID map[string]string,
|
||||||
) (operations.BackupOperation, error) {
|
) (operations.BackupOperation, error) {
|
||||||
gc, err := connectToM365(ctx, sel, r.Account, fault.New(true))
|
gc, err := connectToM365(ctx, sel, r.Account, fault.New(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return operations.BackupOperation{}, errors.Wrap(err, "connecting to m365")
|
return operations.BackupOperation{}, errors.Wrap(err, "connecting to m365")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ownerID, ownerName, err := gc.PopulateOwnerIDAndNamesFrom(sel.DiscreteOwner, ownerIDToName, ownerNameToID)
|
||||||
|
if err != nil {
|
||||||
|
return operations.BackupOperation{}, errors.Wrap(err, "resolving resource owner details")
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: retrieve display name from gc
|
// TODO: retrieve display name from gc
|
||||||
sel = sel.SetDiscreteOwnerIDName(sel.DiscreteOwner, "")
|
sel = sel.SetDiscreteOwnerIDName(ownerID, ownerName)
|
||||||
|
|
||||||
return operations.NewBackupOperation(
|
return operations.NewBackupOperation(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
@ -198,7 +198,7 @@ func (suite *RepositoryIntegrationSuite) TestNewBackup() {
|
|||||||
r, err := repository.Initialize(ctx, acct, st, control.Options{})
|
r, err := repository.Initialize(ctx, acct, st, control.Options{})
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
bo, err := r.NewBackup(ctx, selectors.Selector{DiscreteOwner: "test"})
|
bo, err := r.NewBackup(ctx, selectors.Selector{DiscreteOwner: "test"}, nil, nil)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
require.NotNil(t, bo)
|
require.NotNil(t, bo)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,34 +55,29 @@ func Users(ctx context.Context, acct account.Account, errs *fault.Bus) ([]*User,
|
|||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func UserIDs(ctx context.Context, acct account.Account, errs *fault.Bus) ([]string, error) {
|
// UsersMap retrieves all users in the tenant, and returns two maps: one id-to-principalName,
|
||||||
|
// and one principalName-to-id.
|
||||||
|
func UsersMap(
|
||||||
|
ctx context.Context,
|
||||||
|
acct account.Account,
|
||||||
|
errs *fault.Bus,
|
||||||
|
) (map[string]string, map[string]string, error) {
|
||||||
users, err := Users(ctx, acct, errs)
|
users, err := Users(ctx, acct, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := make([]string, 0, len(users))
|
var (
|
||||||
|
idToName = make(map[string]string, len(users))
|
||||||
|
nameToID = make(map[string]string, len(users))
|
||||||
|
)
|
||||||
|
|
||||||
for _, u := range users {
|
for _, u := range users {
|
||||||
ret = append(ret, u.ID)
|
idToName[u.ID] = u.PrincipalName
|
||||||
|
nameToID[u.PrincipalName] = u.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
return idToName, nameToID, nil
|
||||||
}
|
|
||||||
|
|
||||||
// UserPNs retrieves all user principleNames in the tenant. Principle Names
|
|
||||||
// can be used analogous userIDs in graph API queries.
|
|
||||||
func UserPNs(ctx context.Context, acct account.Account, errs *fault.Bus) ([]string, error) {
|
|
||||||
users, err := Users(ctx, acct, errs)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
ret := make([]string, 0, len(users))
|
|
||||||
for _, u := range users {
|
|
||||||
ret = append(ret, u.PrincipalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Site struct {
|
type Site struct {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user