GC: Restore: Retrieve CalendarID Feature (#687)

Feature added to be able to retrieve containerID for `exchange.Event` types.
This commit is contained in:
Danny 2022-08-31 13:28:57 -04:00 committed by GitHub
parent 7a8c6eaf78
commit b5a5378113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 209 additions and 71 deletions

View File

@ -0,0 +1,28 @@
package exchange
import (
"github.com/microsoftgraph/msgraph-sdk-go/models"
)
// calendarDisplayable is a transformative struct that aligns
// models.Calendarable interface with the displayable interface.
type calendarDisplayable struct {
models.Calendarable
}
// GetDisplayName returns the *string of the calendar name
func (c calendarDisplayable) GetDisplayName() *string {
return c.GetName()
}
// CreateCalendarDisplayable helper function to create the
// calendarDisplayable during msgraph-sdk-go iterative process
// @param entry is the input supplied by pageIterator.Iterate()
func CreateCalendarDisplayable(entry any) *calendarDisplayable {
calendar, ok := entry.(models.Calendarable)
if !ok {
return nil
}
return &calendarDisplayable{calendar}
}

View File

@ -86,6 +86,41 @@ func (suite *ExchangeServiceSuite) TestCreateService() {
}
}
func (suite *ExchangeServiceSuite) TestOptionsForCalendars() {
tests := []struct {
name string
params []string
checkError assert.ErrorAssertionFunc
}{
{
name: "Empty Literal",
params: []string{},
checkError: assert.NoError,
},
{
name: "Invalid Parameter",
params: []string{"status"},
checkError: assert.Error,
},
{
name: "Invalid Parameters",
params: []string{"status", "height", "month"},
checkError: assert.Error,
},
{
name: "Valid Parameters",
params: []string{"changeKey", "events", "owner"},
checkError: assert.NoError,
},
}
for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) {
_, err := optionsForCalendars(test.params)
test.checkError(t, err)
})
}
}
// TestOptionsForMessages checks to ensure approved query
// options are added to the type specific RequestBuildConfiguration. Expected
// will be +1 on all select parameters
@ -255,6 +290,10 @@ func (suite *ExchangeServiceSuite) TestGraphQueryFunctions() {
name: "GraphQuery: Get All Events for User",
function: GetAllEventsForUser,
},
{
name: "GraphQuery: Get All Calendars for User",
function: GetAllCalendarNamesForUser,
},
}
for _, test := range tests {
@ -311,49 +350,61 @@ func (suite *ExchangeServiceSuite) TestParseCalendarIDFromEvent() {
// TestGetMailFolderID verifies the ability to retrieve folder ID of folders
// at the top level of the file tree
func (suite *ExchangeServiceSuite) TestGetFolderID() {
func (suite *ExchangeServiceSuite) TestGetContainerID() {
userID := tester.M365UserID(suite.T())
tests := []struct {
name string
folderName string
name string
containerName string
// category references the current optionId :: TODO --> use selector fields
category optionIdentifier
checkError assert.ErrorAssertionFunc
}{
{
name: "Mail Valid",
folderName: "Inbox",
category: messages,
checkError: assert.NoError,
name: "Mail Valid",
containerName: "Inbox",
category: messages,
checkError: assert.NoError,
},
{
name: "Mail Invalid",
folderName: "FolderThatIsNotHere",
category: messages,
checkError: assert.Error,
name: "Mail Invalid",
containerName: "FolderThatIsNotHere",
category: messages,
checkError: assert.Error,
},
{
name: "Contact Invalid",
folderName: "FolderThatIsNotHereContacts",
category: contacts,
checkError: assert.Error,
name: "Contact Invalid",
containerName: "FolderThatIsNotHereContacts",
category: contacts,
checkError: assert.Error,
},
{
name: "Contact Valid",
folderName: "TrialFolder",
category: contacts,
checkError: assert.NoError,
name: "Contact Valid",
containerName: "TrialFolder",
category: contacts,
checkError: assert.NoError,
},
{
name: "Event Invalid",
containerName: "NotAValid?@V'vCalendar",
category: events,
checkError: assert.Error,
},
{
name: "Event Valid",
containerName: "Calendar",
category: events,
checkError: assert.NoError,
},
}
for _, test := range tests {
suite.T().Run(test.name, func(t *testing.T) {
_, err := GetFolderID(
_, err := GetContainerID(
suite.es,
test.folderName,
test.containerName,
userID,
test.category)
test.checkError(t, err, "Unable to find folder: "+test.folderName)
test.checkError(t, err, "error with container: "+test.containerName)
})
}
}

View File

@ -10,15 +10,16 @@ func _() {
var x [1]struct{}
_ = x[unknown-0]
_ = x[folders-1]
_ = x[events-2]
_ = x[messages-3]
_ = x[users-4]
_ = x[contacts-5]
_ = x[calendars-2]
_ = x[events-3]
_ = x[messages-4]
_ = x[users-5]
_ = x[contacts-6]
}
const _optionIdentifier_name = "unknownfolderseventsmessagesuserscontacts"
const _optionIdentifier_name = "unknownfolderscalendarseventsmessagesuserscontacts"
var _optionIdentifier_index = [...]uint8{0, 7, 14, 20, 28, 33, 41}
var _optionIdentifier_index = [...]uint8{0, 7, 14, 23, 29, 37, 42, 50}
func (i optionIdentifier) String() string {
if i < 0 || i >= optionIdentifier(len(_optionIdentifier_index)-1) {

View File

@ -1,7 +1,10 @@
package exchange
import (
"fmt"
msuser "github.com/microsoftgraph/msgraph-sdk-go/users"
mscalendars "github.com/microsoftgraph/msgraph-sdk-go/users/item/calendars"
mscontactfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/contactfolders"
mscontacts "github.com/microsoftgraph/msgraph-sdk-go/users/item/contacts"
msevents "github.com/microsoftgraph/msgraph-sdk-go/users/item/events"
@ -17,6 +20,15 @@ import (
// selectors for M365 objects
//------------------------------------------------------------
var (
fieldsForCalendars = map[string]int{
"changeKey": 1,
"events": 2,
"id": 3,
"isDefaultCalendar": 4,
"name": 5,
"owner": 6,
}
fieldsForEvents = map[string]int{
"calendar": 1,
"end": 2,
@ -79,6 +91,7 @@ type optionIdentifier int
const (
unknown optionIdentifier = iota
folders
calendars
events
messages
users
@ -104,9 +117,12 @@ func categoryToOptionIdentifier(category string) optionIdentifier {
}
}
//---------------------------------------------------
//---------------------------------------------------------------------------
// exchange.Query Option Section
//------------------------------------------------
// These functions can be used to filter a response on M365
// Graph queries and reduce / filter the amount of data returned
// which reduces the overall latency of complex calls
//----------------------------------------------------------------
// optionsForMessages - used to select allowable options for exchange.Mail types
// @param moreOps is []string of options(e.g. "parentFolderId, subject")
@ -146,6 +162,30 @@ func OptionsForSingleMessage(moreOps []string) (*msitem.MessageItemRequestBuilde
return options, nil
}
// optionsForCalendars places allowed options for exchange.Calendar object
// @param moreOps should reflect elements from fieldsForCalendars
// @return is first call in Calendars().GetWithRequestConfigurationAndResponseHandler
func optionsForCalendars(moreOps []string) (
*mscalendars.CalendarsRequestBuilderGetRequestConfiguration,
error,
) {
selecting, err := buildOptions(moreOps, calendars)
if err != nil {
return nil, err
}
requestParams := &mscalendars.CalendarsRequestBuilderGetQueryParameters{
Select: selecting,
}
options := &mscalendars.CalendarsRequestBuilderGetRequestConfiguration{
QueryParameters: requestParams,
}
return options, nil
}
// optionsForContactFolders places allowed options for exchange.ContactFolder object
// @return is first call in ContactFolders().GetWithRequestConfigurationAndResponseHandler
func optionsForContactFolders(moreOps []string) (
*mscontactfolder.ContactFoldersRequestBuilderGetRequestConfiguration,
error,
@ -246,10 +286,12 @@ func buildOptions(options []string, optID optionIdentifier) ([]string, error) {
)
switch optID {
case events:
allowedOptions = fieldsForEvents
case calendars:
allowedOptions = fieldsForCalendars
case contacts:
allowedOptions = fieldsForContacts
case events:
allowedOptions = fieldsForEvents
case folders:
allowedOptions = fieldsForFolders
case users:
@ -265,7 +307,7 @@ func buildOptions(options []string, optID optionIdentifier) ([]string, error) {
for _, entry := range options {
_, ok := allowedOptions[entry]
if !ok {
return nil, errors.New("unsupported option")
return nil, fmt.Errorf("unsupported option: %v", entry)
}
returnedOptions = append(returnedOptions, entry)

View File

@ -151,16 +151,17 @@ func GetAllMailFolders(gs graph.Service, user, nameContains string) ([]MailFolde
return mfs, err
}
// GetFolderID query function to retrieve the M365 ID based on the folder's displayName.
// @param folderName the target folder's display name. Case sensitive
// GetContainerID query function to retrieve a container's M365 ID.
// @param containerName is the target's name, user-readable and case sensitive
// @param category switches query and iteration to support multiple exchange applications
// @returns a *string if the folder exists. If the folder does not exist returns nil, error-> folder not found
func GetFolderID(service graph.Service, folderName, user string, category optionIdentifier) (*string, error) {
func GetContainerID(service graph.Service, containerName, user string, category optionIdentifier) (*string, error) {
var (
errs error
folderID *string
query GraphQuery
transform absser.ParsableFactory
errs error
targetID *string
query GraphQuery
transform absser.ParsableFactory
isCalendar bool
)
switch category {
@ -170,6 +171,10 @@ func GetFolderID(service graph.Service, folderName, user string, category option
case contacts:
query = GetAllContactFolderNamesForUser
transform = models.CreateContactFolderFromDiscriminatorValue
case events:
query = GetAllCalendarNamesForUser
transform = models.CreateCalendarCollectionResponseFromDiscriminatorValue
isCalendar = true
default:
return nil, fmt.Errorf("unsupported category %s for GetFolderID()", category)
}
@ -192,10 +197,11 @@ func GetFolderID(service graph.Service, folderName, user string, category option
return nil, err
}
callbackFunc := iterateFindFolderID(category,
&folderID,
folderName,
callbackFunc := iterateFindFolderID(
&targetID,
containerName,
service.Adapter().GetBaseUrl(),
isCalendar,
errs,
)
@ -203,11 +209,11 @@ func GetFolderID(service graph.Service, folderName, user string, category option
return nil, support.WrapAndAppend(service.Adapter().GetBaseUrl(), err, errs)
}
if folderID == nil {
if targetID == nil {
return nil, ErrFolderNotFound
}
return folderID, errs
return targetID, errs
}
// parseCalendarIDFromEvent returns the M365 ID for a calendar
@ -298,7 +304,7 @@ func establishFolder(
folderName, user string,
optID optionIdentifier,
) (string, error) {
folderID, err := GetFolderID(service, folderName, user, optID)
folderID, err := GetContainerID(service, folderName, user, optID)
if err == nil {
return *folderID, nil
}

View File

@ -305,44 +305,45 @@ func IterateFilterFolderDirectoriesForCollections(
// the displayable interface. If folder exists, the function updates the
// folderID memory address that was passed in.
func iterateFindFolderID(
category optionIdentifier,
folderID **string,
folderName, errorIdentifier string,
isCalendar bool,
errs error,
) func(any) bool {
return func(entry any) bool {
switch category {
case messages, contacts:
folder, ok := entry.(displayable)
if !ok {
errs = support.WrapAndAppend(
errorIdentifier,
errors.New("struct does not implement displayable"),
errs,
)
if isCalendar {
entry = CreateCalendarDisplayable(entry)
if entry == nil {
return true
}
// Display name not set on folder
if folder.GetDisplayName() == nil {
return true
}
}
name := *folder.GetDisplayName()
if folderName == name {
if folder.GetId() == nil {
return true // invalid folder
}
*folderID = folder.GetId()
return false
}
folder, ok := entry.(displayable)
if !ok {
errs = support.WrapAndAppend(
errorIdentifier,
errors.New("struct does not implement displayable"),
errs,
)
return true
}
// Display name not set on folder
if folder.GetDisplayName() == nil {
return true
}
if folderName == *folder.GetDisplayName() {
if folder.GetId() == nil {
return true // invalid folder
}
*folderID = folder.GetId()
default:
return false
}
return true
}
}

View File

@ -55,6 +55,15 @@ func GetAllFolderNamesForUser(gs graph.Service, user string) (absser.Parsable, e
return gs.Client().UsersById(user).MailFolders().GetWithRequestConfigurationAndResponseHandler(options, nil)
}
func GetAllCalendarNamesForUser(gs graph.Service, user string) (absser.Parsable, error) {
options, err := optionsForCalendars([]string{"name"})
if err != nil {
return nil, err
}
return gs.Client().UsersById(user).Calendars().GetWithRequestConfigurationAndResponseHandler(options, nil)
}
// GetAllContactFolderNamesForUser is a GraphQuery function for getting ContactFolderId
// and display names for contacts. All other information is omitted.
// Does not return the primary Contact Folder