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

View File

@ -90,6 +90,21 @@ type MailFolder struct {
DisplayName string 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. // CreateContactFolder makes a contact folder with the displayName of folderName.
// If successful, returns the created folder object. // If successful, returns the created folder object.
func CreateContactFolder(gs graph.Service, user, folderName string) (models.ContactFolderable, error) { 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 transform = models.CreateCalendarCollectionResponseFromDiscriminatorValue
isCalendar = true isCalendar = true
default: 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) response, err := query(service, user)
@ -197,7 +212,7 @@ func GetContainerID(service graph.Service, containerName, user string, category
return nil, err return nil, err
} }
callbackFunc := iterateFindFolderID( callbackFunc := iterateFindContainerID(
&targetID, &targetID,
containerName, containerName,
service.Adapter().GetBaseUrl(), service.Adapter().GetBaseUrl(),
@ -281,30 +296,18 @@ func SetupExchangeCollectionVars(scope selectors.ExchangeScope) (
return nil, nil, nil, errors.New("exchange scope option not supported") 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 // an unique folder for the restore process
// @param category: input from fullPath()[2] // @param category: input from fullPath()[2]
// that defines the application the folder is created in. // that defines the application the folder is created in.
func GetRestoreFolder( func GetRestoreContainer(
service graph.Service, service graph.Service,
user, category string, user, category string,
) (string, error) { ) (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 { folderID, err := GetContainerID(service, name, user, option)
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)
if err == nil { if err == nil {
return *folderID, nil return *folderID, nil
} }
@ -313,23 +316,30 @@ func establishFolder(
return "", support.WrapAndAppend(user, err, err) return "", support.WrapAndAppend(user, err, err)
} }
switch optID { switch option {
case messages: case messages:
fold, err := CreateMailFolder(service, user, folderName) fold, err := CreateMailFolder(service, user, name)
if err != nil { if err != nil {
return "", support.WrapAndAppend(user, err, err) return "", support.WrapAndAppend(user, err, err)
} }
return *fold.GetId(), nil return *fold.GetId(), nil
case contacts: case contacts:
fold, err := CreateContactFolder(service, user, folderName) fold, err := CreateContactFolder(service, user, name)
if err != nil { if err != nil {
return "", support.WrapAndAppend(user, err, err) return "", support.WrapAndAppend(user, err, err)
} }
return *fold.GetId(), nil 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: 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 // M365 folders objects that matches the folderName. Iterator callback function
// will work on folderCollection responses whose objects implement // will work on folderCollection responses whose objects implement
// the displayable interface. If folder exists, the function updates the // the displayable interface. If folder exists, the function updates the
// folderID memory address that was passed in. // containerID memory address that was passed in.
func iterateFindFolderID( // @param containerName is the string representation of the folder, directory or calendar holds
folderID **string, // the underlying M365 objects
folderName, errorIdentifier string, func iterateFindContainerID(
containerID **string,
containerName, errorIdentifier string,
isCalendar bool, isCalendar bool,
errs error, errs error,
) func(any) bool { ) func(any) bool {
@ -334,12 +336,12 @@ func iterateFindFolderID(
return true return true
} }
if folderName == *folder.GetDisplayName() { if containerName == *folder.GetDisplayName() {
if folder.GetId() == nil { if folder.GetId() == nil {
return true // invalid folder return true // invalid folder
} }
*folderID = folder.GetId() *containerID = folder.GetId()
return false return false
} }

View File

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

View File

@ -321,3 +321,18 @@ func (suite *GraphConnectorIntegrationSuite) TestCreateAndDeleteContactFolder()
assert.NoError(suite.T(), err) 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)
}
}