exchange.Collection to fill in key information from selectors (#457)
Selectors and scope information are used to create the correct populate methods. Populate methods changed to fit type abstractions definitions for iteration, querying, and collection population.
This commit is contained in:
parent
88a8e992fd
commit
bb7531d95c
@ -32,10 +32,8 @@ const (
|
|||||||
RestoreCanonicalEnableValue = "4"
|
RestoreCanonicalEnableValue = "4"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExchangeDataCollection represents exchange mailbox
|
// Collection implements the interface from data.Collection
|
||||||
// data for a single user.
|
// Structure holds data for an Exchange application for a single user
|
||||||
//
|
|
||||||
// It implements the DataCollection interface
|
|
||||||
type Collection struct {
|
type Collection struct {
|
||||||
// M365 user
|
// M365 user
|
||||||
user string // M365 user
|
user string // M365 user
|
||||||
@ -43,34 +41,52 @@ type Collection struct {
|
|||||||
// jobs represents items from the inventory of M365 objectIds whose information
|
// jobs represents items from the inventory of M365 objectIds whose information
|
||||||
// is desired to be sent through the data channel for eventual storage
|
// is desired to be sent through the data channel for eventual storage
|
||||||
jobs []string
|
jobs []string
|
||||||
|
// service - client/adapter pair used to access M365 back store
|
||||||
service graph.Service
|
service graph.Service
|
||||||
|
// populate - Utility function to populate collection based on the M365 application type and granularity
|
||||||
|
populate populater
|
||||||
statusCh chan<- *support.ConnectorOperationStatus
|
statusCh chan<- *support.ConnectorOperationStatus
|
||||||
// FullPath is the slice representation of the action context passed down through the hierarchy.
|
// FullPath is the slice representation of the action context passed down through the hierarchy.
|
||||||
//The original request can be gleaned from the slice. (e.g. {<tenant ID>, <user ID>, "emails"})
|
//The original request can be gleaned from the slice. (e.g. {<tenant ID>, <user ID>, "emails"})
|
||||||
fullPath []string
|
fullPath []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Populater are a class of functions that can be used to fill exchange.Collections with
|
||||||
|
// the corresponding information
|
||||||
|
type populater func(context.Context, graph.Service, *Collection, chan<- *support.ConnectorOperationStatus)
|
||||||
|
|
||||||
// NewExchangeDataCollection creates an ExchangeDataCollection with fullPath is annotated
|
// NewExchangeDataCollection creates an ExchangeDataCollection with fullPath is annotated
|
||||||
func NewCollection(
|
func NewCollection(
|
||||||
aUser string,
|
user string,
|
||||||
pathRepresentation []string,
|
fullPath []string,
|
||||||
collectionType optionIdentifier,
|
collectionType optionIdentifier,
|
||||||
aService graph.Service,
|
service graph.Service,
|
||||||
statusCh chan<- *support.ConnectorOperationStatus,
|
statusCh chan<- *support.ConnectorOperationStatus,
|
||||||
) Collection {
|
) Collection {
|
||||||
collection := Collection{
|
collection := Collection{
|
||||||
user: aUser,
|
user: user,
|
||||||
data: make(chan data.Stream, collectionChannelBufferSize),
|
data: make(chan data.Stream, collectionChannelBufferSize),
|
||||||
jobs: make([]string, 0),
|
jobs: make([]string, 0),
|
||||||
service: aService,
|
service: service,
|
||||||
statusCh: statusCh,
|
statusCh: statusCh,
|
||||||
fullPath: pathRepresentation,
|
fullPath: fullPath,
|
||||||
|
populate: getPopulateFunction(collectionType),
|
||||||
}
|
}
|
||||||
return collection
|
return collection
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddJob appends additional objectID to job field job
|
// getPopulateFunction is a function to set populate function field
|
||||||
|
// with exchange-application specific functions
|
||||||
|
func getPopulateFunction(optId optionIdentifier) populater {
|
||||||
|
switch optId {
|
||||||
|
case messages:
|
||||||
|
return PopulateFromCollection
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddJob appends additional objectID to structure's jobs field
|
||||||
func (eoc *Collection) AddJob(objID string) {
|
func (eoc *Collection) AddJob(objID string) {
|
||||||
eoc.jobs = append(eoc.jobs, objID)
|
eoc.jobs = append(eoc.jobs, objID)
|
||||||
}
|
}
|
||||||
@ -78,18 +94,22 @@ func (eoc *Collection) AddJob(objID string) {
|
|||||||
// Items utility function to asynchronously execute process to fill data channel with
|
// Items utility function to asynchronously execute process to fill data channel with
|
||||||
// M365 exchange objects and returns the data channel
|
// M365 exchange objects and returns the data channel
|
||||||
func (eoc *Collection) Items() <-chan data.Stream {
|
func (eoc *Collection) Items() <-chan data.Stream {
|
||||||
go eoc.PopulateFromCollection(context.TODO(), eoc.service, eoc.statusCh)
|
if eoc.populate != nil {
|
||||||
|
go eoc.populate(context.TODO(), eoc.service, eoc, eoc.statusCh)
|
||||||
|
}
|
||||||
return eoc.data
|
return eoc.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FullPath returns the Collection's fullPath []string
|
||||||
func (edc *Collection) FullPath() []string {
|
func (edc *Collection) FullPath() []string {
|
||||||
return append([]string{}, edc.fullPath...)
|
return append([]string{}, edc.fullPath...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// populateFromTaskList async call to fill DataCollection via channel implementation
|
// PopulateFromCollection async call to fill DataCollection via channel implementation
|
||||||
func (edc *Collection) PopulateFromCollection(
|
func PopulateFromCollection(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
service graph.Service,
|
service graph.Service,
|
||||||
|
edc *Collection,
|
||||||
statusChannel chan<- *support.ConnectorOperationStatus,
|
statusChannel chan<- *support.ConnectorOperationStatus,
|
||||||
) {
|
) {
|
||||||
var errs error
|
var errs error
|
||||||
|
|||||||
@ -2,10 +2,19 @@ package exchange
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/internal/connector/graph"
|
||||||
|
"github.com/alcionai/corso/internal/connector/mockconnector"
|
||||||
|
"github.com/alcionai/corso/internal/connector/support"
|
||||||
|
"github.com/alcionai/corso/internal/data"
|
||||||
|
"github.com/alcionai/corso/pkg/backup/details"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ExchangeDataCollectionSuite struct {
|
type ExchangeDataCollectionSuite struct {
|
||||||
@ -73,3 +82,35 @@ func (suite *ExchangeDataCollectionSuite) TestExchangeCollection_AddJob() {
|
|||||||
suite.Equal(len(shopping), len(eoc.jobs))
|
suite.Equal(len(shopping), len(eoc.jobs))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestExchangeCollection_Items() tests for the Collection.Items() ability
|
||||||
|
// to asynchronously fill `data` field with Stream objects
|
||||||
|
func (suite *ExchangeDataCollectionSuite) TestExchangeCollection_Items() {
|
||||||
|
expected := 5
|
||||||
|
testFunction := func(ctx context.Context,
|
||||||
|
service graph.Service,
|
||||||
|
eoc *Collection,
|
||||||
|
notUsed chan<- *support.ConnectorOperationStatus) {
|
||||||
|
detail := &details.ExchangeInfo{Sender: "foo@bar.com", Subject: "Hello world!", Received: time.Now()}
|
||||||
|
for i := 0; i < expected; i++ {
|
||||||
|
temp := NewStream(uuid.NewString(), mockconnector.GetMockMessageBytes("Test_Items()"), *detail)
|
||||||
|
eoc.data <- &temp
|
||||||
|
}
|
||||||
|
close(eoc.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
eoc := Collection{
|
||||||
|
user: "Dexter",
|
||||||
|
fullPath: []string{"Today", "is", "currently", "different"},
|
||||||
|
data: make(chan data.Stream, expected),
|
||||||
|
populate: testFunction,
|
||||||
|
}
|
||||||
|
t := suite.T()
|
||||||
|
itemsReturn := eoc.Items()
|
||||||
|
retrieved := 0
|
||||||
|
for item := range itemsReturn {
|
||||||
|
assert.NotNil(t, item)
|
||||||
|
retrieved++
|
||||||
|
}
|
||||||
|
suite.Equal(expected, retrieved)
|
||||||
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@ func NewMockExchangeCollection(pathRepresentation []string, numMessagesToReturn
|
|||||||
|
|
||||||
for i := 0; i < c.messageCount; i++ {
|
for i := 0; i < c.messageCount; i++ {
|
||||||
// We can plug in whatever data we want here (can be an io.Reader to a test data file if needed)
|
// We can plug in whatever data we want here (can be an io.Reader to a test data file if needed)
|
||||||
c.Data = append(c.Data, getMockMessageBytes("From: NewMockExchangeCollection"))
|
c.Data = append(c.Data, GetMockMessageBytes("From: NewMockExchangeCollection"))
|
||||||
c.Names = append(c.Names, uuid.NewString())
|
c.Names = append(c.Names, uuid.NewString())
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
@ -85,9 +85,9 @@ func (med *MockExchangeData) Info() details.ItemInfo {
|
|||||||
return details.ItemInfo{Exchange: &details.ExchangeInfo{Sender: "foo@bar.com", Subject: "Hello world!", Received: time.Now()}}
|
return details.ItemInfo{Exchange: &details.ExchangeInfo{Sender: "foo@bar.com", Subject: "Hello world!", Received: time.Now()}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMockMessageBytes returns bytes for Messageable item.
|
// GetMockMessageBytes returns bytes for Messageable item.
|
||||||
// Contents verified as working with sample data from kiota-serialization-json-go v0.5.5
|
// Contents verified as working with sample data from kiota-serialization-json-go v0.5.5
|
||||||
func getMockMessageBytes(subject string) []byte {
|
func GetMockMessageBytes(subject string) []byte {
|
||||||
|
|
||||||
userID, err := tester.M365UserID()
|
userID, err := tester.M365UserID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user