introduce id-name lookup maps (#2955)
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. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🌻 Feature #### Issue(s) * #2825 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
fbb867a08d
commit
2341d61842
@ -13,6 +13,7 @@ import (
|
|||||||
"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"
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/data"
|
"github.com/alcionai/corso/src/internal/data"
|
||||||
"github.com/alcionai/corso/src/pkg/account"
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
"github.com/alcionai/corso/src/pkg/backup"
|
"github.com/alcionai/corso/src/pkg/backup"
|
||||||
@ -194,6 +195,7 @@ func runBackups(
|
|||||||
r repository.Repository,
|
r repository.Repository,
|
||||||
serviceName, resourceOwnerType string,
|
serviceName, resourceOwnerType string,
|
||||||
selectorSet []selectors.Selector,
|
selectorSet []selectors.Selector,
|
||||||
|
ins common.IDNameSwapper,
|
||||||
) error {
|
) error {
|
||||||
var (
|
var (
|
||||||
bIDs []string
|
bIDs []string
|
||||||
@ -203,21 +205,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, ins)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|||||||
@ -164,14 +164,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)
|
ins, 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(ins.IDs()) {
|
||||||
selectorSet = append(selectorSet, discSel.Selector)
|
selectorSet = append(selectorSet, discSel.Selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ func createExchangeCmd(cmd *cobra.Command, args []string) error {
|
|||||||
r,
|
r,
|
||||||
"Exchange", "user",
|
"Exchange", "user",
|
||||||
selectorSet,
|
selectorSet,
|
||||||
)
|
ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exchangeBackupCreateSelectors(userIDs, cats []string) *selectors.ExchangeBackup {
|
func exchangeBackupCreateSelectors(userIDs, cats []string) *selectors.ExchangeBackup {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/cli/config"
|
"github.com/alcionai/corso/src/cli/config"
|
||||||
"github.com/alcionai/corso/src/cli/print"
|
"github.com/alcionai/corso/src/cli/print"
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/connector/exchange"
|
"github.com/alcionai/corso/src/internal/connector/exchange"
|
||||||
"github.com/alcionai/corso/src/internal/operations"
|
"github.com/alcionai/corso/src/internal/operations"
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
@ -300,7 +301,15 @@ 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}
|
||||||
|
ins = common.IDsNames{
|
||||||
|
IDToName: idToName,
|
||||||
|
NameToID: nameToID,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
for _, set := range backupDataSets {
|
for _, set := range backupDataSets {
|
||||||
var (
|
var (
|
||||||
@ -321,7 +330,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, ins)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = bop.Run(ctx)
|
err = bop.Run(ctx)
|
||||||
@ -546,7 +555,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)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = suite.backupOp.Run(ctx)
|
err = suite.backupOp.Run(ctx)
|
||||||
|
|||||||
@ -148,14 +148,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)
|
ins, 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(ins.IDs()) {
|
||||||
selectorSet = append(selectorSet, discSel.Selector)
|
selectorSet = append(selectorSet, discSel.Selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +164,7 @@ func createOneDriveCmd(cmd *cobra.Command, args []string) error {
|
|||||||
r,
|
r,
|
||||||
"OneDrive", "user",
|
"OneDrive", "user",
|
||||||
selectorSet,
|
selectorSet,
|
||||||
)
|
ins)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOneDriveBackupCreateFlags(users []string) error {
|
func validateOneDriveBackupCreateFlags(users []string) error {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/cli/config"
|
"github.com/alcionai/corso/src/cli/config"
|
||||||
"github.com/alcionai/corso/src/cli/print"
|
"github.com/alcionai/corso/src/cli/print"
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/operations"
|
"github.com/alcionai/corso/src/internal/operations"
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
"github.com/alcionai/corso/src/pkg/account"
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
@ -205,14 +206,22 @@ 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}
|
||||||
|
ins = common.IDsNames{
|
||||||
|
IDToName: idToName,
|
||||||
|
NameToID: nameToID,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 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, ins)
|
||||||
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) // TODO: prepopulate ids,names
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateSharePointBackupCreateFlags(sites, weburls, cats []string) error {
|
func validateSharePointBackupCreateFlags(sites, weburls, cats []string) error {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/cli/config"
|
"github.com/alcionai/corso/src/cli/config"
|
||||||
"github.com/alcionai/corso/src/cli/print"
|
"github.com/alcionai/corso/src/cli/print"
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/operations"
|
"github.com/alcionai/corso/src/internal/operations"
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
"github.com/alcionai/corso/src/pkg/account"
|
"github.com/alcionai/corso/src/pkg/account"
|
||||||
@ -157,14 +158,22 @@ 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}
|
||||||
|
ins = common.IDsNames{
|
||||||
|
IDToName: idToName,
|
||||||
|
NameToID: nameToID,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// some tests require an existing backup
|
// some tests require an existing backup
|
||||||
sel := selectors.NewSharePointBackup(sites)
|
sel := selectors.NewSharePointBackup(sites)
|
||||||
sel.Include(testdata.SharePointBackupFolderScope(sel))
|
sel.Include(testdata.SharePointBackupFolderScope(sel))
|
||||||
|
|
||||||
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector)
|
suite.backupOp, err = suite.repo.NewBackup(ctx, sel.Selector, ins)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = suite.backupOp.Run(ctx)
|
err = suite.backupOp.Run(ctx)
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/cli"
|
"github.com/alcionai/corso/src/cli"
|
||||||
"github.com/alcionai/corso/src/cli/config"
|
"github.com/alcionai/corso/src/cli/config"
|
||||||
"github.com/alcionai/corso/src/cli/utils"
|
"github.com/alcionai/corso/src/cli/utils"
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/connector/exchange"
|
"github.com/alcionai/corso/src/internal/connector/exchange"
|
||||||
"github.com/alcionai/corso/src/internal/operations"
|
"github.com/alcionai/corso/src/internal/operations"
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
@ -73,7 +74,16 @@ 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}
|
||||||
|
ins = common.IDsNames{
|
||||||
|
IDToName: idToName,
|
||||||
|
NameToID: nameToID,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// 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 +110,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, ins)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
err = bop.Run(ctx)
|
err = bop.Run(ctx)
|
||||||
|
|||||||
@ -126,12 +126,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)
|
ins, 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 ins.IDs() {
|
||||||
normUsers[strings.ToLower(k)] = struct{}{}
|
normUsers[strings.ToLower(k)] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
|
import "golang.org/x/exp/maps"
|
||||||
|
|
||||||
type IDNamer interface {
|
type IDNamer interface {
|
||||||
// the canonical id of the thing, generated and usable
|
// the canonical id of the thing, generated and usable
|
||||||
// by whichever system has ownership of it.
|
// by whichever system has ownership of it.
|
||||||
@ -7,3 +9,39 @@ type IDNamer interface {
|
|||||||
// the human-readable name of the thing.
|
// the human-readable name of the thing.
|
||||||
Name() string
|
Name() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IDNameSwapper interface {
|
||||||
|
IDOf(name string) (string, bool)
|
||||||
|
NameOf(id string) (string, bool)
|
||||||
|
IDs() []string
|
||||||
|
Names() []string
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ IDNameSwapper = &IDsNames{}
|
||||||
|
|
||||||
|
type IDsNames struct {
|
||||||
|
IDToName map[string]string
|
||||||
|
NameToID map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDOf returns the id associated with the given name.
|
||||||
|
func (in IDsNames) IDOf(name string) (string, bool) {
|
||||||
|
id, ok := in.NameToID[name]
|
||||||
|
return id, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// NameOf returns the name associated with the given id.
|
||||||
|
func (in IDsNames) NameOf(id string) (string, bool) {
|
||||||
|
name, ok := in.IDToName[id]
|
||||||
|
return name, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs returns all known ids.
|
||||||
|
func (in IDsNames) IDs() []string {
|
||||||
|
return maps.Keys(in.IDToName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Names returns all known names.
|
||||||
|
func (in IDsNames) Names() []string {
|
||||||
|
return maps.Keys(in.NameToID)
|
||||||
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/connector/discovery/api"
|
"github.com/alcionai/corso/src/internal/connector/discovery/api"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
@ -49,9 +50,16 @@ 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
|
||||||
|
|
||||||
|
// lookup for 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.
|
||||||
|
IDNameLookup common.IDNameSwapper
|
||||||
|
|
||||||
// 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
|
||||||
@ -108,6 +116,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 swapper is 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
|
||||||
|
ins common.IDNameSwapper,
|
||||||
|
) (string, string, error) {
|
||||||
|
// move this to GC method
|
||||||
|
id, name, err := getOwnerIDAndNameFrom(owner, ins)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", errors.Wrap(err, "resolving resource owner details")
|
||||||
|
}
|
||||||
|
|
||||||
|
gc.IDNameLookup = ins
|
||||||
|
|
||||||
|
if ins == nil || (len(ins.IDs()) == 0 && len(ins.Names()) == 0) {
|
||||||
|
gc.IDNameLookup = common.IDsNames{
|
||||||
|
IDToName: map[string]string{id: name},
|
||||||
|
NameToID: map[string]string{name: id},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id, name, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOwnerIDAndNameFrom(
|
||||||
|
owner string,
|
||||||
|
ins common.IDNameSwapper,
|
||||||
|
) (string, string, error) {
|
||||||
|
if ins == nil {
|
||||||
|
return owner, owner, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, ok := ins.NameOf(owner); ok {
|
||||||
|
return owner, n, nil
|
||||||
|
} else if i, ok := ins.IDOf(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(
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
"github.com/alcionai/corso/src/internal/connector/mockconnector"
|
"github.com/alcionai/corso/src/internal/connector/mockconnector"
|
||||||
"github.com/alcionai/corso/src/internal/connector/support"
|
"github.com/alcionai/corso/src/internal/connector/support"
|
||||||
@ -123,11 +124,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 +136,126 @@ 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
|
||||||
|
ins common.IDsNames
|
||||||
|
expectID string
|
||||||
|
expectName string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil ins",
|
||||||
|
owner: ownerID,
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only id map with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: itn,
|
||||||
|
NameToID: nil,
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only name map with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: nil,
|
||||||
|
NameToID: nti,
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only id map with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: itn,
|
||||||
|
NameToID: nil,
|
||||||
|
},
|
||||||
|
expectID: ownerName,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "only name map with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: nil,
|
||||||
|
NameToID: nti,
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both maps with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: itn,
|
||||||
|
NameToID: nti,
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both maps with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: itn,
|
||||||
|
NameToID: nti,
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerName,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching maps with owner id",
|
||||||
|
owner: ownerID,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
IDToName: map[string]string{"foo": "bar"},
|
||||||
|
NameToID: map[string]string{"fnords": "smarf"},
|
||||||
|
},
|
||||||
|
expectID: ownerID,
|
||||||
|
expectName: ownerID,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-matching with owner name",
|
||||||
|
owner: ownerName,
|
||||||
|
ins: common.IDsNames{
|
||||||
|
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.ins)
|
||||||
|
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()
|
||||||
|
|||||||
@ -110,6 +110,9 @@ func New(
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ResourceOwnerID: ownerID,
|
||||||
|
ResourceOwnerName: ownerName,
|
||||||
|
|
||||||
Version: version.Backup,
|
Version: version.Backup,
|
||||||
SnapshotID: snapshotID,
|
SnapshotID: snapshotID,
|
||||||
StreamStoreID: streamStoreID,
|
StreamStoreID: streamStoreID,
|
||||||
@ -242,10 +245,20 @@ func (b Backup) Values() []string {
|
|||||||
status += (")")
|
status += (")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name := b.ResourceOwnerName
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
name = b.ResourceOwnerID
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(name) == 0 {
|
||||||
|
name = b.Selector.DiscreteOwner
|
||||||
|
}
|
||||||
|
|
||||||
return []string{
|
return []string{
|
||||||
common.FormatTabularDisplayTime(b.StartedAt),
|
common.FormatTabularDisplayTime(b.StartedAt),
|
||||||
string(b.ID),
|
string(b.ID),
|
||||||
status,
|
status,
|
||||||
b.Selector.DiscreteOwner,
|
name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ func TestBackupUnitSuite(t *testing.T) {
|
|||||||
suite.Run(t, &BackupUnitSuite{Suite: tester.NewUnitSuite(t)})
|
suite.Run(t, &BackupUnitSuite{Suite: tester.NewUnitSuite(t)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func stubBackup(t time.Time) backup.Backup {
|
func stubBackup(t time.Time, ownerID, ownerName string) backup.Backup {
|
||||||
sel := selectors.NewExchangeBackup([]string{"test"})
|
sel := selectors.NewExchangeBackup([]string{"test"})
|
||||||
sel.Include(sel.AllData())
|
sel.Include(sel.AllData())
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ func (suite *BackupUnitSuite) TestBackup_HeadersValues() {
|
|||||||
var (
|
var (
|
||||||
t = suite.T()
|
t = suite.T()
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
b = stubBackup(now)
|
b = stubBackup(now, "id", "name")
|
||||||
expectHs = []string{
|
expectHs = []string{
|
||||||
"Started At",
|
"Started At",
|
||||||
"ID",
|
"ID",
|
||||||
@ -190,7 +190,7 @@ func (suite *BackupUnitSuite) TestBackup_Values_statusVariations() {
|
|||||||
func (suite *BackupUnitSuite) TestBackup_MinimumPrintable() {
|
func (suite *BackupUnitSuite) TestBackup_MinimumPrintable() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
b := stubBackup(now)
|
b := stubBackup(now, "id", "name")
|
||||||
|
|
||||||
resultIface := b.MinimumPrintable()
|
resultIface := b.MinimumPrintable()
|
||||||
result, ok := resultIface.(backup.Printable)
|
result, ok := resultIface.(backup.Printable)
|
||||||
|
|||||||
@ -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)
|
||||||
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)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/crash"
|
"github.com/alcionai/corso/src/internal/common/crash"
|
||||||
"github.com/alcionai/corso/src/internal/connector"
|
"github.com/alcionai/corso/src/internal/connector"
|
||||||
"github.com/alcionai/corso/src/internal/connector/graph"
|
"github.com/alcionai/corso/src/internal/connector/graph"
|
||||||
@ -58,6 +59,7 @@ type Repository interface {
|
|||||||
NewBackup(
|
NewBackup(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
self selectors.Selector,
|
self selectors.Selector,
|
||||||
|
ins common.IDNameSwapper,
|
||||||
) (operations.BackupOperation, error)
|
) (operations.BackupOperation, error)
|
||||||
NewRestore(
|
NewRestore(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
@ -287,17 +289,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,
|
||||||
|
ins common.IDNameSwapper,
|
||||||
) (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, ins)
|
||||||
|
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)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
require.NotNil(t, bo)
|
require.NotNil(t, bo)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common"
|
||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
"github.com/alcionai/corso/src/internal/connector"
|
"github.com/alcionai/corso/src/internal/connector"
|
||||||
"github.com/alcionai/corso/src/internal/connector/discovery"
|
"github.com/alcionai/corso/src/internal/connector/discovery"
|
||||||
@ -56,34 +57,34 @@ 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,
|
||||||
|
) (common.IDsNames, error) {
|
||||||
users, err := Users(ctx, acct, errs)
|
users, err := Users(ctx, acct, errs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return common.IDsNames{}, 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
|
ins := common.IDsNames{
|
||||||
}
|
IDToName: idToName,
|
||||||
|
NameToID: nameToID,
|
||||||
// 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))
|
return ins, nil
|
||||||
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