GC: Restore: Create / Delete Calendar Feature (#708)

Feature creation: Create/Delete Calendar for aM365 user added to main branch
This commit is contained in:
Danny 2022-08-31 18:03:54 -04:00 committed by GitHub
parent 5c59522fc2
commit 8ead744e9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 68 deletions

View File

@ -5,12 +5,12 @@ import (
"testing"
"time"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/internal/common"
"github.com/alcionai/corso/internal/connector/graph"
"github.com/alcionai/corso/internal/connector/mockconnector"
"github.com/alcionai/corso/internal/tester"
"github.com/alcionai/corso/pkg/account"
@ -430,61 +430,57 @@ func (suite *ExchangeServiceSuite) TestRestoreContact() {
assert.NoError(t, err)
}
// TestEstablishFolder checks the ability to Create a "container" for the
// TestGetRestoreContainer checks the ability to Create a "container" for the
// GraphConnector's Restore Workflow based on OptionIdentifier.
func (suite *ExchangeServiceSuite) TestEstablishFolder() {
func (suite *ExchangeServiceSuite) TestGetRestoreContainer() {
tests := []struct {
name string
option optionIdentifier
checkError assert.ErrorAssertionFunc
name string
option string
checkError assert.ErrorAssertionFunc
cleanupFunc func(graph.Service, string, string) error
}{
{
name: "Establish User Restore Folder",
option: users,
checkError: assert.Error,
name: "Establish User Restore Folder",
option: "users",
checkError: assert.Error,
cleanupFunc: nil,
},
{
name: "Establish Event Restore Location",
option: events,
checkError: assert.Error,
name: "Establish Event Restore Location",
option: "events",
checkError: assert.NoError,
cleanupFunc: DeleteCalendar,
},
{
name: "Establish Restore Folder for Unknown",
option: unknown,
checkError: assert.Error,
name: "Establish Restore Folder for Unknown",
option: "unknown",
checkError: assert.Error,
cleanupFunc: nil,
},
{
name: "Establish Restore folder for Mail",
option: messages,
checkError: assert.NoError,
name: "Establish Restore folder for Mail",
option: "mail",
checkError: assert.NoError,
cleanupFunc: DeleteMailFolder,
},
{
name: "Establish Restore folder for Contacts",
option: contacts,
checkError: assert.NoError,
name: "Establish Restore folder for Contacts",
option: "contacts",
checkError: assert.NoError,
cleanupFunc: DeleteContactFolder,
},
}
now := time.Now()
folderName := "CorsoEstablishFolder" + common.FormatSimpleDateTime(now)
userID := tester.M365UserID(suite.T())
for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) {
folderID, err := establishFolder(suite.es, folderName, userID, test.option)
containerID, err := GetRestoreContainer(suite.es, userID, test.option)
require.True(t, test.checkError(t, err))
if folderID != "" {
switch test.option {
case messages:
err = DeleteMailFolder(suite.es, userID, folderID)
assert.NoError(t, err)
case contacts:
err = DeleteContactFolder(suite.es, userID, folderID)
assert.NoError(t, err)
default:
assert.NoError(t,
errors.New("unsupported type received folderID: "+test.option.String()),
)
}
if test.cleanupFunc != nil {
err = test.cleanupFunc(suite.es, userID, containerID)
assert.NoError(t, err)
}
})
}

View File

@ -90,6 +90,21 @@ type MailFolder struct {
DisplayName string
}
// CreateCalendar makes an event Calendar with the name in the user's M365 exchange account
// Reference: https://docs.microsoft.com/en-us/graph/api/user-post-calendars?view=graph-rest-1.0&tabs=go
func CreateCalendar(gs graph.Service, user, calendarName string) (models.Calendarable, error) {
requestbody := models.NewCalendar()
requestbody.SetName(&calendarName)
return gs.Client().UsersById(user).Calendars().Post(requestbody)
}
// DeleteCalendar removes calendar from user's M365 account
// Reference: https://docs.microsoft.com/en-us/graph/api/calendar-delete?view=graph-rest-1.0&tabs=go
func DeleteCalendar(gs graph.Service, user, calendarID string) error {
return gs.Client().UsersById(user).CalendarsById(calendarID).Delete()
}
// CreateContactFolder makes a contact folder with the displayName of folderName.
// If successful, returns the created folder object.
func CreateContactFolder(gs graph.Service, user, folderName string) (models.ContactFolderable, error) {
@ -176,7 +191,7 @@ func GetContainerID(service graph.Service, containerName, user string, category
transform = models.CreateCalendarCollectionResponseFromDiscriminatorValue
isCalendar = true
default:
return nil, fmt.Errorf("unsupported category %s for GetFolderID()", category)
return nil, fmt.Errorf("unsupported category %s for GetContainerID()", category)
}
response, err := query(service, user)
@ -197,7 +212,7 @@ func GetContainerID(service graph.Service, containerName, user string, category
return nil, err
}
callbackFunc := iterateFindFolderID(
callbackFunc := iterateFindContainerID(
&targetID,
containerName,
service.Adapter().GetBaseUrl(),
@ -281,30 +296,18 @@ func SetupExchangeCollectionVars(scope selectors.ExchangeScope) (
return nil, nil, nil, errors.New("exchange scope option not supported")
}
// GetRestoreFolder utility function to create
// GetRestoreContainer utility function to create
// an unique folder for the restore process
// @param category: input from fullPath()[2]
// that defines the application the folder is created in.
func GetRestoreFolder(
func GetRestoreContainer(
service graph.Service,
user, category string,
) (string, error) {
newFolder := fmt.Sprintf("Corso_Restore_%s", common.FormatNow(common.SimpleDateTimeFormat))
name := fmt.Sprintf("Corso_Restore_%s", common.FormatNow(common.SimpleDateTimeFormat))
option := categoryToOptionIdentifier(category)
switch category {
case mailCategory, contactsCategory:
return establishFolder(service, newFolder, user, categoryToOptionIdentifier(category))
default:
return "", fmt.Errorf("%s category not supported", category)
}
}
func establishFolder(
service graph.Service,
folderName, user string,
optID optionIdentifier,
) (string, error) {
folderID, err := GetContainerID(service, folderName, user, optID)
folderID, err := GetContainerID(service, name, user, option)
if err == nil {
return *folderID, nil
}
@ -313,23 +316,30 @@ func establishFolder(
return "", support.WrapAndAppend(user, err, err)
}
switch optID {
switch option {
case messages:
fold, err := CreateMailFolder(service, user, folderName)
fold, err := CreateMailFolder(service, user, name)
if err != nil {
return "", support.WrapAndAppend(user, err, err)
}
return *fold.GetId(), nil
case contacts:
fold, err := CreateContactFolder(service, user, folderName)
fold, err := CreateContactFolder(service, user, name)
if err != nil {
return "", support.WrapAndAppend(user, err, err)
}
return *fold.GetId(), nil
case events:
calendar, err := CreateCalendar(service, user, name)
if err != nil {
return "", support.WrapAndAppend(user, err, err)
}
return *calendar.GetId(), nil
default:
return "", fmt.Errorf("category: %s not supported for folder creation", optID)
return "", fmt.Errorf("category: %s not supported for folder creation", option)
}
}

View File

@ -299,14 +299,16 @@ func IterateFilterFolderDirectoriesForCollections(
}
}
// iterateFindFolderID is a utility function that supports finding
// iterateFindContainerID is a utility function that supports finding
// M365 folders objects that matches the folderName. Iterator callback function
// will work on folderCollection responses whose objects implement
// the displayable interface. If folder exists, the function updates the
// folderID memory address that was passed in.
func iterateFindFolderID(
folderID **string,
folderName, errorIdentifier string,
// containerID memory address that was passed in.
// @param containerName is the string representation of the folder, directory or calendar holds
// the underlying M365 objects
func iterateFindContainerID(
containerID **string,
containerName, errorIdentifier string,
isCalendar bool,
errs error,
) func(any) bool {
@ -334,12 +336,12 @@ func iterateFindFolderID(
return true
}
if folderName == *folder.GetDisplayName() {
if containerName == *folder.GetDisplayName() {
if folder.GetId() == nil {
return true // invalid folder
}
*folderID = folder.GetId()
*containerID = folder.GetId()
return false
}

View File

@ -263,7 +263,7 @@ func (gc *GraphConnector) RestoreExchangeDataCollection(
if _, ok := pathCounter[directory]; !ok {
pathCounter[directory] = true
folderID, errs = exchange.GetRestoreFolder(&gc.graphService, user, category)
folderID, errs = exchange.GetRestoreContainer(&gc.graphService, user, category)
if errs != nil {
return errs

View File

@ -321,3 +321,18 @@ func (suite *GraphConnectorIntegrationSuite) TestCreateAndDeleteContactFolder()
assert.NoError(suite.T(), err)
}
}
// TestCreateAndDeleteCalendar verifies GraphConnector has the ability to create and remove
// exchange.Event.Calendars within the tenant
func (suite *GraphConnectorIntegrationSuite) TestCreateAndDeleteCalendar() {
now := time.Now()
service := suite.connector.Service()
calendarName := "TestCalendar: " + common.FormatSimpleDateTime(now)
calendar, err := exchange.CreateCalendar(service, suite.user, calendarName)
assert.NoError(suite.T(), err)
if calendar != nil {
err = exchange.DeleteCalendar(service, suite.user, *calendar.GetId())
assert.NoError(suite.T(), err)
}
}