Exchange service: Abstractions (#450)
- Creation of exchange service-based query file. Test package expanded for support
This commit is contained in:
parent
bd559f690e
commit
890c8f2b7f
93
src/internal/connector/exchange/exchange_service_test.go
Normal file
93
src/internal/connector/exchange/exchange_service_test.go
Normal file
@ -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))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
26
src/internal/connector/exchange/optionidentifier_string.go
Normal file
26
src/internal/connector/exchange/optionidentifier_string.go
Normal file
@ -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]]
|
||||||
|
}
|
||||||
129
src/internal/connector/exchange/service_query.go
Normal file
129
src/internal/connector/exchange/service_query.go
Normal file
@ -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
|
||||||
|
}
|
||||||
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
package connector
|
package connector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
|
|
||||||
msfolder "github.com/microsoftgraph/msgraph-sdk-go/users/item/mailfolders"
|
|
||||||
msmessage "github.com/microsoftgraph/msgraph-sdk-go/users/item/messages"
|
msmessage "github.com/microsoftgraph/msgraph-sdk-go/users/item/messages"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,24 +25,6 @@ func Contains(elems []string, value string) bool {
|
|||||||
return false
|
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 {
|
func optionsForMessageSnapshot() *msmessage.MessagesRequestBuilderGetRequestConfiguration {
|
||||||
selecting := []string{"id", "parentFolderId"}
|
selecting := []string{"id", "parentFolderId"}
|
||||||
options := &msmessage.MessagesRequestBuilderGetRequestConfiguration{
|
options := &msmessage.MessagesRequestBuilderGetRequestConfiguration{
|
||||||
@ -55,49 +34,3 @@ func optionsForMessageSnapshot() *msmessage.MessagesRequestBuilderGetRequestConf
|
|||||||
}
|
}
|
||||||
return options
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user