package graph import ( "context" "github.com/alcionai/clues" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/path" ) // Idable represents objects that implement msgraph-sdk-go/models.entityable // and have the concept of an ID. type Idable interface { GetId() *string } // Descendable represents objects that implement msgraph-sdk-go/models.entityable // and have the concept of a "parent folder". type Descendable interface { Idable GetParentFolderId() *string } // Displayable represents objects that implement msgraph-sdk-go/models.entityable // and have the concept of a display name. type Displayable interface { Idable GetDisplayName() *string } type Container interface { Descendable Displayable } // CachedContainer is used for local unit tests but also makes it so that this // code can be broken into generic- and service-specific chunks later on to // reuse logic in IDToPath. type CachedContainer interface { Container // Location contains either the display names for the dirs (if this is a calendar) // or nil Location() *path.Builder SetLocation(*path.Builder) // Path contains either the ids for the dirs (if this is a calendar) // or the display names for the dirs Path() *path.Builder SetPath(*path.Builder) } // ContainerResolver houses functions for getting information about containers // from remote APIs (i.e. resolve folder paths with Graph API). Resolvers may // cache information about containers. type ContainerResolver interface { // IDToPath takes an m365 container ID and converts it to a hierarchical path // to that container. The path has a similar format to paths on the local // file system. IDToPath(ctx context.Context, m365ID string) (*path.Builder, *path.Builder, error) // Populate performs initialization steps for the resolver // @param ctx is necessary param for Graph API tracing // @param baseFolderID represents the M365ID base that the resolver will // conclude its search. Default input is "". Populate(ctx context.Context, errs *fault.Bus, baseFolderID string, baseContainerPath ...string) error // PathInCache performs a look up of a path representation // and returns the m365ID of directory iff the pathString // matches the path of a container within the cache. // @returns bool represents if m365ID was found. PathInCache(pathString string) (string, bool) // LocationInCache performs a look up of a path representation // and returns the m365ID of directory iff the pathString // matches the logical path of a container within the cache. // @returns bool represents if m365ID was found. LocationInCache(pathString string) (string, bool) AddToCache(ctx context.Context, m365Container Container) error // Items returns the containers in the cache. Items() []CachedContainer } // ====================================== // cachedContainer Implementations // ====================================== var _ CachedContainer = &CacheFolder{} type CacheFolder struct { Container l *path.Builder p *path.Builder } // NewCacheFolder public constructor for struct func NewCacheFolder(c Container, pb, lpb *path.Builder) CacheFolder { cf := CacheFolder{ Container: c, l: lpb, p: pb, } return cf } // ========================================= // Required Functions to satisfy interfaces // ========================================= func (cf CacheFolder) Location() *path.Builder { return cf.l } func (cf *CacheFolder) SetLocation(newLocation *path.Builder) { cf.l = newLocation } func (cf CacheFolder) Path() *path.Builder { return cf.p } func (cf *CacheFolder) SetPath(newPath *path.Builder) { cf.p = newPath } // CalendarDisplayable is a transformative struct that aligns // models.Calendarable interface with the container interface. // Calendars do not have the 2 of the type CalendarDisplayable struct { models.Calendarable parentID string } // GetDisplayName returns the *string of the calendar name func (c CalendarDisplayable) GetDisplayName() *string { return c.GetName() } // GetParentFolderId returns the default calendar name address // EventCalendars have a flat hierarchy and Calendars are rooted // at the default // //nolint:revive func (c CalendarDisplayable) GetParentFolderId() *string { return &c.parentID } // CreateCalendarDisplayable helper function to create the // calendarDisplayable during msgraph-sdk-go iterative process // @param entry is the input supplied by pageIterator.Iterate() // @param parentID of Calendar sets. Only populate when used with // EventCalendarCache func CreateCalendarDisplayable(entry any, parentID string) *CalendarDisplayable { calendar, ok := entry.(models.Calendarable) if !ok { return nil } return &CalendarDisplayable{ Calendarable: calendar, parentID: parentID, } } // ========================================= // helper funcs // ========================================= // CheckIDAndName is a validator that ensures the ID // and name are populated and not zero valued. func CheckIDAndName(c Container) error { if c == nil { return clues.New("nil container") } id := ptr.Val(c.GetId()) if len(id) == 0 { return clues.New("container missing ID") } dn := ptr.Val(c.GetDisplayName()) if len(dn) == 0 { return clues.New("container missing display name").With("container_id", id) } return nil } // CheckIDNameAndParentFolderID is a validator that ensures the ID // and name are populated and not zero valued. func CheckIDNameAndParentFolderID(c Container) error { err := CheckIDAndName(c) if err != nil { return err } pfid := ptr.Val(c.GetParentFolderId()) if len(pfid) == 0 { return clues.New("container missing parent folder id").With("container_id", ptr.Val(c.GetId())) } return nil }