diff --git a/src/internal/connector/exchange/exchange_service_test.go b/src/internal/connector/exchange/exchange_service_test.go new file mode 100644 index 000000000..c117503f7 --- /dev/null +++ b/src/internal/connector/exchange/exchange_service_test.go @@ -0,0 +1,93 @@ +package exchange + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +type ExchangeServiceSuite struct { + suite.Suite +} + +func TestExchangeServiceSuite(t *testing.T) { + suite.Run(t, new(ExchangeServiceSuite)) +} + +// TestExchangeService_optionsForMessages checks to ensure approved query +// options are added to the type specific RequestBuildConfiguration. Expected +// will be +1 on all select parameters +func (suite *ExchangeServiceSuite) TestExchangeService_optionsForMessages() { + tests := []struct { + name string + params []string + checkError assert.ErrorAssertionFunc + }{ + { + name: "Accepted", + params: []string{"subject"}, + checkError: assert.NoError, + }, + { + name: "Multiple Accepted", + params: []string{"webLink", "parentFolderId"}, + checkError: assert.NoError, + }, + { + name: "Incorrect param", + params: []string{"status"}, + checkError: assert.Error, + }, + } + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + config, err := optionsForMessages(test.params) + test.checkError(t, err) + if err == nil { + suite.Equal(len(config.QueryParameters.Select), len(test.params)+1) + } + }) + } +} + +// TestExchangeService_optionsForFolders ensures that approved query options +// are added to the RequestBuildConfiguration. Expected will always be +1 +// on than the input as "id" are always included within the select parameters +func (suite *ExchangeServiceSuite) TestExchangeService_optionsForFolders() { + tests := []struct { + name string + params []string + checkError assert.ErrorAssertionFunc + expected int + }{ + { + name: "Accepted", + params: []string{"displayName"}, + checkError: assert.NoError, + expected: 2, + }, + { + name: "Multiple Accepted", + params: []string{"displayName", "parentFolderId"}, + checkError: assert.NoError, + expected: 3, + }, + { + name: "Incorrect param", + params: []string{"status"}, + checkError: assert.Error, + }, + } + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + config, err := optionsForMailFolders(test.params) + test.checkError(t, err) + if err == nil { + suite.Equal(test.expected, len(config.QueryParameters.Select)) + } + }) + + } + +} diff --git a/src/internal/connector/exchange/optionidentifier_string.go b/src/internal/connector/exchange/optionidentifier_string.go new file mode 100644 index 000000000..08344aa92 --- /dev/null +++ b/src/internal/connector/exchange/optionidentifier_string.go @@ -0,0 +1,26 @@ +// Code generated by "stringer -type=optionIdentifier"; DO NOT EDIT. + +package exchange + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[unknown-0] + _ = x[folders-1] + _ = x[messages-2] + _ = x[users-3] +} + +const _optionIdentifier_name = "unknownfoldersmessagesusers" + +var _optionIdentifier_index = [...]uint8{0, 7, 14, 22, 27} + +func (i optionIdentifier) String() string { + if i < 0 || i >= optionIdentifier(len(_optionIdentifier_index)-1) { + return "optionIdentifier(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _optionIdentifier_name[_optionIdentifier_index[i]:_optionIdentifier_index[i+1]] +} diff --git a/src/internal/connector/exchange/service_query.go b/src/internal/connector/exchange/service_query.go new file mode 100644 index 000000000..f111e2475 --- /dev/null +++ b/src/internal/connector/exchange/service_query.go @@ -0,0 +1,129 @@ +package exchange + +import ( + absser "github.com/microsoft/kiota-abstractions-go/serialization" + msfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/mailfolders" + msmessage "github.com/microsoftgraph/msgraph-sdk-go/users/item/messages" + "github.com/pkg/errors" + + "github.com/alcionai/corso/internal/connector/graph" +) + +type optionIdentifier int + +//go:generate stringer -type=optionIdentifier +const ( + unknown optionIdentifier = iota + folders + messages + users +) + +// GraphQuery represents functions which perform exchange-specific queries +// into M365 backstore. +//TODO: use selector or path for granularity into specific folders or specific date ranges +type GraphQuery func(graph.Service, []string) (absser.Parsable, error) + +// GetAllMessagesForUser is a GraphQuery function for receiving all messages for a single user +func GetAllMessagesForUser(gs graph.Service, identities []string) (absser.Parsable, error) { + selecting := []string{"id", "parentFolderId"} + options, err := optionsForMessages(selecting) + if err != nil { + return nil, err + } + return gs.Client().UsersById(identities[0]).Messages().GetWithRequestConfigurationAndResponseHandler(options, nil) +} + +//--------------------------------------------------- +// exchange.Query Option Section +//------------------------------------------------ + +// optionsForMessages - used to select allowable options for exchange.Mail types +// @param moreOps is []string of options(e.g. "parentFolderId, subject") +// @return is first call in Messages().GetWithRequestConfigurationAndResponseHandler +func optionsForMessages(moreOps []string) (*msmessage.MessagesRequestBuilderGetRequestConfiguration, error) { + selecting, err := buildOptions(moreOps, messages) + if err != nil { + return nil, err + } + requestParameters := &msmessage.MessagesRequestBuilderGetQueryParameters{ + Select: selecting, + } + options := &msmessage.MessagesRequestBuilderGetRequestConfiguration{ + QueryParameters: requestParameters, + } + return options, nil +} + +// optionsForMailFolders transforms the options into a more dynamic call for MailFolders. +// @param moreOps is a []string of options(e.g. "displayName", "isHidden") +// @return is first call in MailFolders().GetWithRequestConfigurationAndResponseHandler(options, handler) +func optionsForMailFolders(moreOps []string) (*msfolder.MailFoldersRequestBuilderGetRequestConfiguration, error) { + selecting, err := buildOptions(moreOps, folders) + if err != nil { + return nil, err + } + + requestParameters := &msfolder.MailFoldersRequestBuilderGetQueryParameters{ + Select: selecting, + } + options := &msfolder.MailFoldersRequestBuilderGetRequestConfiguration{ + QueryParameters: requestParameters, + } + return options, nil +} + +// buildOptions - Utility Method for verifying if select options are valid for the m365 object type +// @return is a pair. The first is a string literal of allowable options based on the object type, +// the second is an error. An error is returned if an unsupported option or optionIdentifier was used +func buildOptions(options []string, optId optionIdentifier) ([]string, error) { + var allowedOptions map[string]int + + fieldsForFolders := map[string]int{ + "displayName": 1, + "isHidden": 2, + "parentFolderId": 3, + } + + fieldsForUsers := map[string]int{ + "birthday": 1, + "businessPhones": 2, + "city": 3, + "companyName": 4, + "department": 5, + "displayName": 6, + "employeeId": 7, + } + + fieldsForMessages := map[string]int{ + "conservationId": 1, + "conversationIndex": 2, + "parentFolderId": 3, + "subject": 4, + "webLink": 5, + } + returnedOptions := []string{"id"} + + switch optId { + case folders: + allowedOptions = fieldsForFolders + case users: + allowedOptions = fieldsForUsers + case messages: + allowedOptions = fieldsForMessages + case unknown: + fallthrough + default: + return nil, errors.New("unsupported option") + } + + for _, entry := range options { + _, ok := allowedOptions[entry] + if ok { + returnedOptions = append(returnedOptions, entry) + } else { + return nil, errors.New("unsupported option") + } + } + return returnedOptions, nil +} diff --git a/src/internal/connector/graph_connector_disconnected_test.go b/src/internal/connector/graph_connector_disconnected_test.go index 71951847d..73fce3efd 100644 --- a/src/internal/connector/graph_connector_disconnected_test.go +++ b/src/internal/connector/graph_connector_disconnected_test.go @@ -160,65 +160,3 @@ func (suite *DisconnectedGraphConnectorSuite) TestGraphConnector_ErrorChecking() }) } } - -func (suite *DisconnectedGraphConnectorSuite) TestGraphConnector_TestOptionsForMailFolders() { - tests := []struct { - name string - params []string - isError bool - }{ - { - name: "Accepted", - params: []string{"displayName"}, - isError: false, - }, - { - name: "Multiple Accepted", - params: []string{"displayName", "parentFolderId"}, - isError: false, - }, - { - name: "Incorrect param", - params: []string{"status"}, - isError: true, - }, - } - for _, test := range tests { - suite.T().Run(test.name, func(t *testing.T) { - _, err := optionsForMailFolders(test.params) - suite.Equal(test.isError, err != nil) - }) - - } - -} - -func (suite *DisconnectedGraphConnectorSuite) TestGraphConnector_TestOptionsForMessages() { - tests := []struct { - name string - params []string - isError bool - }{ - { - name: "Accepted", - params: []string{"subject"}, - isError: false, - }, - { - name: "Multiple Accepted", - params: []string{"webLink", "parentFolderId"}, - isError: false, - }, - { - name: "Incorrect param", - params: []string{"status"}, - isError: true, - }, - } - for _, test := range tests { - suite.T().Run(test.name, func(t *testing.T) { - _, err := optionsForMessages(test.params) - suite.Equal(test.isError, err != nil) - }) - } -} diff --git a/src/internal/connector/query.go b/src/internal/connector/query.go index 94ea0a53a..d39d64678 100644 --- a/src/internal/connector/query.go +++ b/src/internal/connector/query.go @@ -1,9 +1,6 @@ package connector import ( - "errors" - - msfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/mailfolders" msmessage "github.com/microsoftgraph/msgraph-sdk-go/users/item/messages" ) @@ -28,24 +25,6 @@ func Contains(elems []string, value string) bool { return false } -// optionsForMailFolders creates transforms the 'select' into a more dynamic call for MailFolders. -// var moreOps is a []string of options(e.g. "displayName", "isHidden") -// return is first call in MailFolders().GetWithRequestConfigurationAndResponseHandler(options, handler) -func optionsForMailFolders(moreOps []string) (*msfolder.MailFoldersRequestBuilderGetRequestConfiguration, error) { - selecting, err := buildOptions(moreOps, folders) - if err != nil { - return nil, err - } - - requestParameters := &msfolder.MailFoldersRequestBuilderGetQueryParameters{ - Select: selecting, - } - options := &msfolder.MailFoldersRequestBuilderGetRequestConfiguration{ - QueryParameters: requestParameters, - } - return options, nil -} - func optionsForMessageSnapshot() *msmessage.MessagesRequestBuilderGetRequestConfiguration { selecting := []string{"id", "parentFolderId"} options := &msmessage.MessagesRequestBuilderGetRequestConfiguration{ @@ -55,49 +34,3 @@ func optionsForMessageSnapshot() *msmessage.MessagesRequestBuilderGetRequestConf } return options } - -func optionsForMessages(moreOps []string) (*msmessage.MessagesRequestBuilderGetRequestConfiguration, error) { - selecting, err := buildOptions(moreOps, messages) - if err != nil { - return nil, err - } - requestParameters := &msmessage.MessagesRequestBuilderGetQueryParameters{ - Select: selecting, - } - options := &msmessage.MessagesRequestBuilderGetRequestConfiguration{ - QueryParameters: requestParameters, - } - return options, nil -} - -// CheckOptions Utility Method for verifying if select options are valid the m365 object type -// returns a list of valid options -func buildOptions(options []string, selection optionIdentifier) ([]string, error) { - var allowedOptions []string - - fieldsForFolders := []string{"displayName", "isHidden", "parentFolderId", "totalItemCount"} - fieldsForUsers := []string{"birthday", "businessPhones", "city", "companyName", "department", "displayName", "employeeId"} - fieldsForMessages := []string{"conservationId", "conversationIndex", "parentFolderId", "subject", "webLink"} - returnedOptions := []string{"id"} - - switch selection { - case folders: - allowedOptions = fieldsForFolders - case users: - allowedOptions = fieldsForUsers - case messages: - allowedOptions = fieldsForMessages - default: - return nil, errors.New("unsupported option") - } - - for _, entry := range options { - result := Contains(allowedOptions, entry) - if result { - returnedOptions = append(returnedOptions, entry) - } else { - return nil, errors.New("unsupported option") - } - } - return returnedOptions, nil -}