ensure od and exchange can restore in-place (#3703)
Adds changes (mostly to exchange) that allow in- place item restoration. Onedrive basically already supported in-place restores, however we weren't attempting them. Exchange needed full support of GetContainerByName across its categories. Next steps: add CLI integration to simplify manual testing. Then add automation testing to exercise all in-place restore conditions and configurations. --- #### Does this PR need a docs update or release note? - [x] 🕐 Yes, but in a later PR #### Type of change - [x] 🌻 Feature #### Issue(s) * #3562 #### Test Plan - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
6fff3f7d1d
commit
8cc3069a6a
@ -47,13 +47,16 @@ func (h contactRestoreHandler) formatRestoreDestination(
|
|||||||
|
|
||||||
func (h contactRestoreHandler) CreateContainer(
|
func (h contactRestoreHandler) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName, _ string, // parent container not used
|
userID, _, containerName string, // parent container not used
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
return h.ac.CreateContainer(ctx, userID, containerName, "")
|
return h.ac.CreateContainer(ctx, userID, "", containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h contactRestoreHandler) containerSearcher() containerByNamer {
|
func (h contactRestoreHandler) GetContainerByName(
|
||||||
return nil
|
ctx context.Context,
|
||||||
|
userID, _, containerName string, // parent container not used
|
||||||
|
) (graph.Container, error) {
|
||||||
|
return h.ac.GetContainerByName(ctx, userID, "", containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// always returns the provided value
|
// always returns the provided value
|
||||||
|
|||||||
@ -48,13 +48,16 @@ func (h eventRestoreHandler) formatRestoreDestination(
|
|||||||
|
|
||||||
func (h eventRestoreHandler) CreateContainer(
|
func (h eventRestoreHandler) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName, _ string, // parent container not used
|
userID, _, containerName string, // parent container not used
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
return h.ac.CreateContainer(ctx, userID, containerName, "")
|
return h.ac.CreateContainer(ctx, userID, "", containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h eventRestoreHandler) containerSearcher() containerByNamer {
|
func (h eventRestoreHandler) GetContainerByName(
|
||||||
return h.ac
|
ctx context.Context,
|
||||||
|
userID, _, containerName string, // parent container not used
|
||||||
|
) (graph.Container, error) {
|
||||||
|
return h.ac.GetContainerByName(ctx, userID, "", containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// always returns the provided value
|
// always returns the provided value
|
||||||
|
|||||||
@ -86,19 +86,14 @@ type itemRestorer interface {
|
|||||||
// produces structs that interface with the graph/cache_container
|
// produces structs that interface with the graph/cache_container
|
||||||
// CachedContainer interface.
|
// CachedContainer interface.
|
||||||
type containerAPI interface {
|
type containerAPI interface {
|
||||||
|
containerByNamer
|
||||||
|
|
||||||
// POSTs the creation of a new container
|
// POSTs the creation of a new container
|
||||||
CreateContainer(
|
CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName, parentContainerID string,
|
userID, parentContainerID, containerName string,
|
||||||
) (graph.Container, error)
|
) (graph.Container, error)
|
||||||
|
|
||||||
// GETs a container by name.
|
|
||||||
// if containerByNamer is nil, this functionality is not supported
|
|
||||||
// and should be skipped by the caller.
|
|
||||||
// normally, we'd alias the func directly. The indirection here
|
|
||||||
// is because not all types comply with GetContainerByName.
|
|
||||||
containerSearcher() containerByNamer
|
|
||||||
|
|
||||||
// returns either the provided value (assumed to be the root
|
// returns either the provided value (assumed to be the root
|
||||||
// folder for that cache tree), or the default root container
|
// folder for that cache tree), or the default root container
|
||||||
// (if the category uses a root folder that exists above the
|
// (if the category uses a root folder that exists above the
|
||||||
@ -110,7 +105,7 @@ type containerByNamer interface {
|
|||||||
// searches for a container by name.
|
// searches for a container by name.
|
||||||
GetContainerByName(
|
GetContainerByName(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName string,
|
userID, parentContainerID, containerName string,
|
||||||
) (graph.Container, error)
|
) (graph.Container, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,17 +48,20 @@ func (h mailRestoreHandler) formatRestoreDestination(
|
|||||||
|
|
||||||
func (h mailRestoreHandler) CreateContainer(
|
func (h mailRestoreHandler) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName, parentContainerID string,
|
userID, parentContainerID, containerName string,
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
if len(parentContainerID) == 0 {
|
if len(parentContainerID) == 0 {
|
||||||
parentContainerID = rootFolderAlias
|
parentContainerID = rootFolderAlias
|
||||||
}
|
}
|
||||||
|
|
||||||
return h.ac.CreateContainer(ctx, userID, containerName, parentContainerID)
|
return h.ac.CreateContainer(ctx, userID, parentContainerID, containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h mailRestoreHandler) containerSearcher() containerByNamer {
|
func (h mailRestoreHandler) GetContainerByName(
|
||||||
return nil
|
ctx context.Context,
|
||||||
|
userID, parentContainerID, containerName string,
|
||||||
|
) (graph.Container, error) {
|
||||||
|
return h.ac.GetContainerByName(ctx, userID, parentContainerID, containerName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// always returns rootFolderAlias
|
// always returns rootFolderAlias
|
||||||
|
|||||||
@ -44,6 +44,7 @@ func ConsumeRestoreCollections(
|
|||||||
el = errs.Local()
|
el = errs.Local()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FIXME: should be user name
|
||||||
ctx = clues.Add(ctx, "resource_owner", clues.Hide(userID))
|
ctx = clues.Add(ctx, "resource_owner", clues.Hide(userID))
|
||||||
|
|
||||||
for _, dc := range dcs {
|
for _, dc := range dcs {
|
||||||
@ -289,7 +290,7 @@ func getOrPopulateContainer(
|
|||||||
return cached, nil
|
return cached, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := ca.CreateContainer(ctx, userID, containerName, containerParentID)
|
c, err := ca.CreateContainer(ctx, userID, containerParentID, containerName)
|
||||||
|
|
||||||
// 409 handling case:
|
// 409 handling case:
|
||||||
// attempt to fetch the container by name and add that result to the cache.
|
// attempt to fetch the container by name and add that result to the cache.
|
||||||
@ -297,11 +298,12 @@ func getOrPopulateContainer(
|
|||||||
// sometimes the backend will create the folder despite the 5xx response,
|
// sometimes the backend will create the folder despite the 5xx response,
|
||||||
// leaving our local containerResolver with inconsistent state.
|
// leaving our local containerResolver with inconsistent state.
|
||||||
if graph.IsErrFolderExists(err) {
|
if graph.IsErrFolderExists(err) {
|
||||||
cs := ca.containerSearcher()
|
cc, e := ca.GetContainerByName(ctx, userID, containerParentID, containerName)
|
||||||
if cs != nil {
|
if e != nil {
|
||||||
cc, e := cs.GetContainerByName(ctx, userID, containerName)
|
|
||||||
c = cc
|
|
||||||
err = clues.Stack(err, e)
|
err = clues.Stack(err, e)
|
||||||
|
} else {
|
||||||
|
c = cc
|
||||||
|
err = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -60,7 +60,7 @@ func (suite *RestoreIntgSuite) TestRestoreContact() {
|
|||||||
handler = newContactRestoreHandler(suite.ac)
|
handler = newContactRestoreHandler(suite.ac)
|
||||||
)
|
)
|
||||||
|
|
||||||
aFolder, err := handler.ac.CreateContainer(ctx, userID, folderName, "")
|
aFolder, err := handler.ac.CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
folderID := ptr.Val(aFolder.GetId())
|
folderID := ptr.Val(aFolder.GetId())
|
||||||
@ -96,7 +96,7 @@ func (suite *RestoreIntgSuite) TestRestoreEvent() {
|
|||||||
handler = newEventRestoreHandler(suite.ac)
|
handler = newEventRestoreHandler(suite.ac)
|
||||||
)
|
)
|
||||||
|
|
||||||
calendar, err := handler.ac.CreateContainer(ctx, userID, subject, "")
|
calendar, err := handler.ac.CreateContainer(ctx, userID, "", subject)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
calendarID := ptr.Val(calendar.GetId())
|
calendarID := ptr.Val(calendar.GetId())
|
||||||
@ -179,7 +179,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailobj").Location
|
folderName := testdata.DefaultRestoreConfig("mailobj").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -192,7 +192,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailwattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailwattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -205,7 +205,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("eventwattch").Location
|
folderName := testdata.DefaultRestoreConfig("eventwattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -218,7 +218,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailitemattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailitemattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -234,7 +234,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailbasicattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailbasicattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -250,7 +250,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailnestattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailnestattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -266,7 +266,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailcontactattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailcontactattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -279,7 +279,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("nestedattch").Location
|
folderName := testdata.DefaultRestoreConfig("nestedattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -292,7 +292,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("maillargeattch").Location
|
folderName := testdata.DefaultRestoreConfig("maillargeattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -305,7 +305,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailtwoattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailtwoattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -318,7 +318,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("mailrefattch").Location
|
folderName := testdata.DefaultRestoreConfig("mailrefattch").Location
|
||||||
folder, err := handlers[path.EmailCategory].
|
folder, err := handlers[path.EmailCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -331,7 +331,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("contact").Location
|
folderName := testdata.DefaultRestoreConfig("contact").Location
|
||||||
folder, err := handlers[path.ContactsCategory].
|
folder, err := handlers[path.ContactsCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(folder.GetId())
|
return ptr.Val(folder.GetId())
|
||||||
@ -344,7 +344,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("event").Location
|
folderName := testdata.DefaultRestoreConfig("event").Location
|
||||||
calendar, err := handlers[path.EventsCategory].
|
calendar, err := handlers[path.EventsCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(calendar.GetId())
|
return ptr.Val(calendar.GetId())
|
||||||
@ -357,7 +357,7 @@ func (suite *RestoreIntgSuite) TestRestoreExchangeObject() {
|
|||||||
destination: func(t *testing.T, ctx context.Context) string {
|
destination: func(t *testing.T, ctx context.Context) string {
|
||||||
folderName := testdata.DefaultRestoreConfig("eventobj").Location
|
folderName := testdata.DefaultRestoreConfig("eventobj").Location
|
||||||
calendar, err := handlers[path.EventsCategory].
|
calendar, err := handlers[path.EventsCategory].
|
||||||
CreateContainer(ctx, userID, folderName, "")
|
CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
return ptr.Val(calendar.GetId())
|
return ptr.Val(calendar.GetId())
|
||||||
@ -400,7 +400,7 @@ func (suite *RestoreIntgSuite) TestRestoreAndBackupEvent_recurringInstancesWithA
|
|||||||
handler = newEventRestoreHandler(suite.ac)
|
handler = newEventRestoreHandler(suite.ac)
|
||||||
)
|
)
|
||||||
|
|
||||||
calendar, err := handler.ac.CreateContainer(ctx, userID, subject, "")
|
calendar, err := handler.ac.CreateContainer(ctx, userID, "", subject)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
calendarID := ptr.Val(calendar.GetId())
|
calendarID := ptr.Val(calendar.GetId())
|
||||||
|
|||||||
@ -37,8 +37,8 @@ type Contacts struct {
|
|||||||
// If successful, returns the created folder object.
|
// If successful, returns the created folder object.
|
||||||
func (c Contacts) CreateContainer(
|
func (c Contacts) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName string,
|
// parentContainerID needed for iface, doesn't apply to contacts
|
||||||
_ string, // parentContainerID needed for iface, doesn't apply to contacts
|
userID, _, containerName string,
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
body := models.NewContactFolder()
|
body := models.NewContactFolder()
|
||||||
body.SetDisplayName(ptr.To(containerName))
|
body.SetDisplayName(ptr.To(containerName))
|
||||||
@ -117,6 +117,56 @@ func (c Contacts) GetContainerByID(
|
|||||||
return c.GetFolder(ctx, userID, containerID)
|
return c.GetFolder(ctx, userID, containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContainerByName fetches a folder by name
|
||||||
|
func (c Contacts) GetContainerByName(
|
||||||
|
ctx context.Context,
|
||||||
|
// parentContainerID needed for iface, doesn't apply to contacts
|
||||||
|
userID, _, containerName string,
|
||||||
|
) (graph.Container, error) {
|
||||||
|
filter := fmt.Sprintf("displayName eq '%s'", containerName)
|
||||||
|
options := &users.ItemContactFoldersRequestBuilderGetRequestConfiguration{
|
||||||
|
QueryParameters: &users.ItemContactFoldersRequestBuilderGetQueryParameters{
|
||||||
|
Filter: &filter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = clues.Add(ctx, "container_name", containerName)
|
||||||
|
|
||||||
|
resp, err := c.Stable.
|
||||||
|
Client().
|
||||||
|
Users().
|
||||||
|
ByUserId(userID).
|
||||||
|
ContactFolders().
|
||||||
|
Get(ctx, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Stack(ctx, err).WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
gv := resp.GetValue()
|
||||||
|
|
||||||
|
if len(gv) == 0 {
|
||||||
|
return nil, clues.New("container not found").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only allow the api to match one container with the provided name.
|
||||||
|
// Return an error if multiple container exist (unlikely) or if no container
|
||||||
|
// is found.
|
||||||
|
if len(gv) != 1 {
|
||||||
|
return nil, clues.New("unexpected number of folders returned").
|
||||||
|
With("returned_container_count", len(gv)).
|
||||||
|
WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check ID and name
|
||||||
|
container := gv[0]
|
||||||
|
|
||||||
|
if err := graph.CheckIDAndName(container); err != nil {
|
||||||
|
return nil, clues.Stack(err).WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Contacts) PatchFolder(
|
func (c Contacts) PatchFolder(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package api
|
package api_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -7,11 +7,15 @@ 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/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
exchMock "github.com/alcionai/corso/src/internal/m365/exchange/mock"
|
exchMock "github.com/alcionai/corso/src/internal/m365/exchange/mock"
|
||||||
"github.com/alcionai/corso/src/internal/tester"
|
"github.com/alcionai/corso/src/internal/tester"
|
||||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
|
"github.com/alcionai/corso/src/pkg/control/testdata"
|
||||||
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContactsAPIUnitSuite struct {
|
type ContactsAPIUnitSuite struct {
|
||||||
@ -64,7 +68,7 @@ func (suite *ContactsAPIUnitSuite) TestContactInfo() {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
contact, expected := test.contactAndRP()
|
contact, expected := test.contactAndRP()
|
||||||
assert.Equal(suite.T(), expected, ContactInfo(contact))
|
assert.Equal(suite.T(), expected, api.ContactInfo(contact))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,9 +103,72 @@ func (suite *ContactsAPIUnitSuite) TestBytesToContactable() {
|
|||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
result, err := BytesToContactable(test.byteArray)
|
result, err := api.BytesToContactable(test.byteArray)
|
||||||
test.checkError(t, err, clues.ToCore(err))
|
test.checkError(t, err, clues.ToCore(err))
|
||||||
test.isNil(t, result)
|
test.isNil(t, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ContactsAPIIntgSuite struct {
|
||||||
|
tester.Suite
|
||||||
|
its intgTesterSetup
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContactsAPIntgSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &ContactsAPIIntgSuite{
|
||||||
|
Suite: tester.NewIntegrationSuite(
|
||||||
|
t,
|
||||||
|
[][]string{tester.M365AcctCredEnvs}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ContactsAPIIntgSuite) SetupSuite() {
|
||||||
|
suite.its = newIntegrationTesterSetup(suite.T())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ContactsAPIIntgSuite) TestContacts_GetContainerByName() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
// contacts cannot filter for the parent "contacts" folder, so we
|
||||||
|
// have to hack this by creating a folder to match beforehand.
|
||||||
|
|
||||||
|
rc := testdata.DefaultRestoreConfig("contacts_api")
|
||||||
|
|
||||||
|
cc, err := suite.its.ac.Contacts().CreateContainer(
|
||||||
|
ctx,
|
||||||
|
suite.its.userID,
|
||||||
|
"",
|
||||||
|
rc.Location)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: ptr.Val(cc.GetDisplayName()),
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "smarfs",
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
_, err := suite.its.ac.
|
||||||
|
Contacts().
|
||||||
|
GetContainerByName(ctx, suite.its.userID, "", test.name)
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/alcionai/corso/src/internal/m365/graph"
|
"github.com/alcionai/corso/src/internal/m365/graph"
|
||||||
"github.com/alcionai/corso/src/pkg/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
"github.com/alcionai/corso/src/pkg/fault"
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
|
"github.com/alcionai/corso/src/pkg/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@ -44,8 +45,8 @@ type Events struct {
|
|||||||
// Reference: https://docs.microsoft.com/en-us/graph/api/user-post-calendars?view=graph-rest-1.0&tabs=go
|
// Reference: https://docs.microsoft.com/en-us/graph/api/user-post-calendars?view=graph-rest-1.0&tabs=go
|
||||||
func (c Events) CreateContainer(
|
func (c Events) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName string,
|
// parentContainerID needed for iface, doesn't apply to events
|
||||||
_ string, // parentContainerID needed for iface, doesn't apply to contacts
|
userID, _, containerName string,
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
body := models.NewCalendar()
|
body := models.NewCalendar()
|
||||||
body.SetName(&containerName)
|
body.SetName(&containerName)
|
||||||
@ -132,7 +133,8 @@ func (c Events) GetContainerByID(
|
|||||||
// GetContainerByName fetches a calendar by name
|
// GetContainerByName fetches a calendar by name
|
||||||
func (c Events) GetContainerByName(
|
func (c Events) GetContainerByName(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName string,
|
// parentContainerID needed for iface, doesn't apply to events
|
||||||
|
userID, _, containerName string,
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
filter := fmt.Sprintf("name eq '%s'", containerName)
|
filter := fmt.Sprintf("name eq '%s'", containerName)
|
||||||
options := &users.ItemCalendarsRequestBuilderGetRequestConfiguration{
|
options := &users.ItemCalendarsRequestBuilderGetRequestConfiguration{
|
||||||
@ -141,7 +143,7 @@ func (c Events) GetContainerByName(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = clues.Add(ctx, "calendar_name", containerName)
|
ctx = clues.Add(ctx, "container_name", containerName)
|
||||||
|
|
||||||
resp, err := c.Stable.
|
resp, err := c.Stable.
|
||||||
Client().
|
Client().
|
||||||
@ -153,24 +155,25 @@ func (c Events) GetContainerByName(
|
|||||||
return nil, graph.Stack(ctx, err).WithClues(ctx)
|
return nil, graph.Stack(ctx, err).WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only allow the api to match one calendar with provided name.
|
gv := resp.GetValue()
|
||||||
// Return an error if multiple calendars exist (unlikely) or if no calendar
|
|
||||||
// is found.
|
if len(gv) == 0 {
|
||||||
if len(resp.GetValue()) != 1 {
|
return nil, clues.New("container not found").WithClues(ctx)
|
||||||
err = clues.New("unexpected number of calendars returned").
|
|
||||||
With("returned_calendar_count", len(resp.GetValue()))
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We only allow the api to match one calendar with the provided name.
|
||||||
|
// If we match multiples, we'll eagerly return the first one.
|
||||||
|
logger.Ctx(ctx).Debugw("calendars matched the name search", "calendar_count", len(gv))
|
||||||
|
|
||||||
// Sanity check ID and name
|
// Sanity check ID and name
|
||||||
cal := resp.GetValue()[0]
|
cal := gv[0]
|
||||||
cd := CalendarDisplayable{Calendarable: cal}
|
container := graph.CalendarDisplayable{Calendarable: cal}
|
||||||
|
|
||||||
if err := graph.CheckIDAndName(cd); err != nil {
|
if err := graph.CheckIDAndName(container); err != nil {
|
||||||
return nil, err
|
return nil, clues.Stack(err).WithClues(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return graph.CalendarDisplayable{Calendarable: cal}, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Events) PatchCalendar(
|
func (c Events) PatchCalendar(
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alcionai/clues"
|
"github.com/alcionai/clues"
|
||||||
|
"github.com/h2non/gock"
|
||||||
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
"github.com/microsoftgraph/msgraph-sdk-go/models"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -235,7 +236,7 @@ func (suite *EventsAPIIntgSuite) SetupSuite() {
|
|||||||
suite.its = newIntegrationTesterSetup(suite.T())
|
suite.its = newIntegrationTesterSetup(suite.T())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *EventsAPIIntgSuite) TestRestoreLargeAttachment() {
|
func (suite *EventsAPIIntgSuite) TestEvents_RestoreLargeAttachment() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
ctx, flush := tester.NewContext(t)
|
ctx, flush := tester.NewContext(t)
|
||||||
@ -245,7 +246,7 @@ func (suite *EventsAPIIntgSuite) TestRestoreLargeAttachment() {
|
|||||||
|
|
||||||
folderName := testdata.DefaultRestoreConfig("eventlargeattachmenttest").Location
|
folderName := testdata.DefaultRestoreConfig("eventlargeattachmenttest").Location
|
||||||
evts := suite.its.ac.Events()
|
evts := suite.its.ac.Events()
|
||||||
calendar, err := evts.CreateContainer(ctx, userID, folderName, "")
|
calendar, err := evts.CreateContainer(ctx, userID, "", folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
tomorrow := time.Now().Add(24 * time.Hour)
|
tomorrow := time.Now().Add(24 * time.Hour)
|
||||||
@ -287,7 +288,7 @@ func (suite *EventsAPIIntgSuite) TestEvents_canFindNonStandardFolder() {
|
|||||||
ac := suite.its.ac.Events()
|
ac := suite.its.ac.Events()
|
||||||
rc := testdata.DefaultRestoreConfig("api_calendar_discovery")
|
rc := testdata.DefaultRestoreConfig("api_calendar_discovery")
|
||||||
|
|
||||||
cal, err := ac.CreateContainer(ctx, suite.its.userID, rc.Location, "")
|
cal, err := ac.CreateContainer(ctx, suite.its.userID, "", rc.Location)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -316,3 +317,91 @@ func (suite *EventsAPIIntgSuite) TestEvents_canFindNonStandardFolder() {
|
|||||||
"If this fails, the user's calendars have probably broken, "+
|
"If this fails, the user's calendars have probably broken, "+
|
||||||
"and the user will need to be rotated")
|
"and the user will need to be rotated")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *EventsAPIIntgSuite) TestEvents_GetContainerByName() {
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Calendar",
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "smarfs",
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
_, err := suite.its.ac.
|
||||||
|
Events().
|
||||||
|
GetContainerByName(ctx, suite.its.userID, "", test.name)
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *EventsAPIIntgSuite) TestEvents_GetContainerByName_mocked() {
|
||||||
|
c := models.NewCalendar()
|
||||||
|
c.SetId(ptr.To("id"))
|
||||||
|
c.SetName(ptr.To("display name"))
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
results func(*testing.T) map[string]any
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
return parseableToMap(t, models.NewCalendarCollectionResponse())
|
||||||
|
},
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
mfcr := models.NewCalendarCollectionResponse()
|
||||||
|
mfcr.SetValue([]models.Calendarable{c})
|
||||||
|
|
||||||
|
return parseableToMap(t, mfcr)
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
mfcr := models.NewCalendarCollectionResponse()
|
||||||
|
mfcr.SetValue([]models.Calendarable{c, c})
|
||||||
|
|
||||||
|
return parseableToMap(t, mfcr)
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
|
||||||
|
defer flush()
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
interceptV1Path("users", "u", "calendars").
|
||||||
|
Reply(200).
|
||||||
|
JSON(test.results(t))
|
||||||
|
|
||||||
|
_, err := suite.its.gockAC.
|
||||||
|
Events().
|
||||||
|
GetContainerByName(ctx, "u", "", test.name)
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
assert.True(t, gock.IsDone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -82,7 +82,7 @@ func (c Mail) DeleteMailFolder(
|
|||||||
|
|
||||||
func (c Mail) CreateContainer(
|
func (c Mail) CreateContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerName, parentContainerID string,
|
userID, parentContainerID, containerName string,
|
||||||
) (graph.Container, error) {
|
) (graph.Container, error) {
|
||||||
isHidden := false
|
isHidden := false
|
||||||
body := models.NewMailFolder()
|
body := models.NewMailFolder()
|
||||||
@ -165,6 +165,75 @@ func (c Mail) GetContainerByID(
|
|||||||
return c.GetFolder(ctx, userID, containerID)
|
return c.GetFolder(ctx, userID, containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetContainerByName fetches a folder by name
|
||||||
|
func (c Mail) GetContainerByName(
|
||||||
|
ctx context.Context,
|
||||||
|
userID, parentContainerID, containerName string,
|
||||||
|
) (graph.Container, error) {
|
||||||
|
filter := fmt.Sprintf("displayName eq '%s'", containerName)
|
||||||
|
|
||||||
|
ctx = clues.Add(ctx, "container_name", containerName)
|
||||||
|
|
||||||
|
var (
|
||||||
|
builder = c.Stable.
|
||||||
|
Client().
|
||||||
|
Users().
|
||||||
|
ByUserId(userID).
|
||||||
|
MailFolders()
|
||||||
|
resp models.MailFolderCollectionResponseable
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(parentContainerID) > 0 {
|
||||||
|
options := &users.ItemMailFoldersItemChildFoldersRequestBuilderGetRequestConfiguration{
|
||||||
|
QueryParameters: &users.ItemMailFoldersItemChildFoldersRequestBuilderGetQueryParameters{
|
||||||
|
Filter: &filter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = builder.
|
||||||
|
ByMailFolderId(parentContainerID).
|
||||||
|
ChildFolders().
|
||||||
|
Get(ctx, options)
|
||||||
|
} else {
|
||||||
|
options := &users.ItemMailFoldersRequestBuilderGetRequestConfiguration{
|
||||||
|
QueryParameters: &users.ItemMailFoldersRequestBuilderGetQueryParameters{
|
||||||
|
Filter: &filter,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = builder.Get(ctx, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, graph.Stack(ctx, err).WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
gv := resp.GetValue()
|
||||||
|
|
||||||
|
if len(gv) == 0 {
|
||||||
|
return nil, clues.New("container not found").WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only allow the api to match one container with the provided name.
|
||||||
|
// Return an error if multiple container exist (unlikely) or if no container
|
||||||
|
// is found.
|
||||||
|
if len(gv) != 1 {
|
||||||
|
return nil, clues.New("unexpected number of folders returned").
|
||||||
|
With("returned_container_count", len(gv)).
|
||||||
|
WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanity check ID and name
|
||||||
|
container := gv[0]
|
||||||
|
|
||||||
|
if err := graph.CheckIDAndName(container); err != nil {
|
||||||
|
return nil, clues.Stack(err).WithClues(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Mail) MoveContainer(
|
func (c Mail) MoveContainer(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
userID, containerID string,
|
userID, containerID string,
|
||||||
|
|||||||
@ -14,12 +14,10 @@ import (
|
|||||||
"github.com/alcionai/corso/src/internal/common/ptr"
|
"github.com/alcionai/corso/src/internal/common/ptr"
|
||||||
exchMock "github.com/alcionai/corso/src/internal/m365/exchange/mock"
|
exchMock "github.com/alcionai/corso/src/internal/m365/exchange/mock"
|
||||||
"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/backup/details"
|
"github.com/alcionai/corso/src/pkg/backup/details"
|
||||||
"github.com/alcionai/corso/src/pkg/control/testdata"
|
"github.com/alcionai/corso/src/pkg/control/testdata"
|
||||||
"github.com/alcionai/corso/src/pkg/fault"
|
"github.com/alcionai/corso/src/pkg/fault"
|
||||||
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
"github.com/alcionai/corso/src/pkg/services/m365/api"
|
||||||
"github.com/alcionai/corso/src/pkg/services/m365/api/mock"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MailAPIUnitSuite struct {
|
type MailAPIUnitSuite struct {
|
||||||
@ -189,9 +187,7 @@ func (suite *MailAPIUnitSuite) TestBytesToMessagable() {
|
|||||||
|
|
||||||
type MailAPIIntgSuite struct {
|
type MailAPIIntgSuite struct {
|
||||||
tester.Suite
|
tester.Suite
|
||||||
credentials account.M365Config
|
its intgTesterSetup
|
||||||
ac api.Client
|
|
||||||
user string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do end up mocking the actual request, but creating the rest
|
// We do end up mocking the actual request, but creating the rest
|
||||||
@ -205,17 +201,7 @@ func TestMailAPIIntgSuite(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MailAPIIntgSuite) SetupSuite() {
|
func (suite *MailAPIIntgSuite) SetupSuite() {
|
||||||
t := suite.T()
|
suite.its = newIntegrationTesterSetup(suite.T())
|
||||||
|
|
||||||
a := tester.NewM365Account(t)
|
|
||||||
m365, err := a.M365Config()
|
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
|
||||||
|
|
||||||
suite.credentials = m365
|
|
||||||
suite.ac, err = mock.NewClient(m365)
|
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
|
||||||
|
|
||||||
suite.user = tester.M365UserID(t)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() {
|
func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() {
|
||||||
@ -353,7 +339,12 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() {
|
|||||||
defer gock.Off()
|
defer gock.Off()
|
||||||
tt.setupf()
|
tt.setupf()
|
||||||
|
|
||||||
item, _, err := suite.ac.Mail().GetItem(ctx, "user", mid, false, fault.New(true))
|
item, _, err := suite.its.gockAC.Mail().GetItem(
|
||||||
|
ctx,
|
||||||
|
"user",
|
||||||
|
mid,
|
||||||
|
false,
|
||||||
|
fault.New(true))
|
||||||
tt.expect(t, err)
|
tt.expect(t, err)
|
||||||
|
|
||||||
it, ok := item.(models.Messageable)
|
it, ok := item.(models.Messageable)
|
||||||
@ -381,7 +372,7 @@ func (suite *MailAPIIntgSuite) TestHugeAttachmentListDownload() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *MailAPIIntgSuite) TestRestoreLargeAttachment() {
|
func (suite *MailAPIIntgSuite) TestMail_RestoreLargeAttachment() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
ctx, flush := tester.NewContext(t)
|
ctx, flush := tester.NewContext(t)
|
||||||
@ -390,7 +381,7 @@ func (suite *MailAPIIntgSuite) TestRestoreLargeAttachment() {
|
|||||||
userID := tester.M365UserID(suite.T())
|
userID := tester.M365UserID(suite.T())
|
||||||
|
|
||||||
folderName := testdata.DefaultRestoreConfig("maillargeattachmenttest").Location
|
folderName := testdata.DefaultRestoreConfig("maillargeattachmenttest").Location
|
||||||
msgs := suite.ac.Mail()
|
msgs := suite.its.ac.Mail()
|
||||||
mailfolder, err := msgs.CreateMailFolder(ctx, userID, folderName)
|
mailfolder, err := msgs.CreateMailFolder(ctx, userID, folderName)
|
||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
@ -411,3 +402,127 @@ func (suite *MailAPIIntgSuite) TestRestoreLargeAttachment() {
|
|||||||
require.NoError(t, err, clues.ToCore(err))
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
require.NotEmpty(t, id, "empty id for large attachment")
|
require.NotEmpty(t, id, "empty id for large attachment")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *MailAPIIntgSuite) TestMail_GetContainerByName() {
|
||||||
|
var (
|
||||||
|
t = suite.T()
|
||||||
|
acm = suite.its.ac.Mail()
|
||||||
|
rc = testdata.DefaultRestoreConfig("mail_get_container_by_name")
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
parent, err := acm.CreateContainer(ctx, suite.its.userID, "msgfolderroot", rc.Location)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
parentContainerID string
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Inbox",
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "smarfs",
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: rc.Location,
|
||||||
|
parentContainerID: ptr.Val(parent.GetId()),
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Inbox",
|
||||||
|
parentContainerID: ptr.Val(parent.GetId()),
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
_, err := acm.GetContainerByName(ctx, suite.its.userID, test.parentContainerID, test.name)
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.Run("child folder with same name", func() {
|
||||||
|
pid := ptr.Val(parent.GetId())
|
||||||
|
t := suite.T()
|
||||||
|
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
defer flush()
|
||||||
|
|
||||||
|
child, err := acm.CreateContainer(ctx, suite.its.userID, pid, rc.Location)
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
result, err := acm.GetContainerByName(ctx, suite.its.userID, pid, rc.Location)
|
||||||
|
assert.NoError(t, err, clues.ToCore(err))
|
||||||
|
assert.Equal(t, ptr.Val(child.GetId()), ptr.Val(result.GetId()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MailAPIIntgSuite) TestMail_GetContainerByName_mocked() {
|
||||||
|
mf := models.NewMailFolder()
|
||||||
|
mf.SetId(ptr.To("id"))
|
||||||
|
mf.SetDisplayName(ptr.To("display name"))
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
results func(*testing.T) map[string]any
|
||||||
|
expectErr assert.ErrorAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zero",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
return parseableToMap(t, models.NewMailFolderCollectionResponse())
|
||||||
|
},
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "one",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
mfcr := models.NewMailFolderCollectionResponse()
|
||||||
|
mfcr.SetValue([]models.MailFolderable{mf})
|
||||||
|
|
||||||
|
return parseableToMap(t, mfcr)
|
||||||
|
},
|
||||||
|
expectErr: assert.NoError,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "two",
|
||||||
|
results: func(t *testing.T) map[string]any {
|
||||||
|
mfcr := models.NewMailFolderCollectionResponse()
|
||||||
|
mfcr.SetValue([]models.MailFolderable{mf, mf})
|
||||||
|
|
||||||
|
return parseableToMap(t, mfcr)
|
||||||
|
},
|
||||||
|
expectErr: assert.Error,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.Run(test.name, func() {
|
||||||
|
t := suite.T()
|
||||||
|
ctx, flush := tester.NewContext(t)
|
||||||
|
|
||||||
|
defer flush()
|
||||||
|
defer gock.Off()
|
||||||
|
|
||||||
|
interceptV1Path("users", "u", "mailFolders").
|
||||||
|
Reply(200).
|
||||||
|
JSON(test.results(t))
|
||||||
|
|
||||||
|
_, err := suite.its.gockAC.
|
||||||
|
Mail().
|
||||||
|
GetContainerByName(ctx, "u", "", test.name)
|
||||||
|
test.expectErr(t, err, clues.ToCore(err))
|
||||||
|
assert.True(t, gock.IsDone())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user