corso/src/pkg/backup/details/builder.go
Keepers e3c51b7dc9
add basic groups boilerplate (#3971)
Adding in some basic boilerplate for groups service.

---

#### Does this PR need a docs update or release note?

- [x]  No

#### Type of change

- [x] 🌻 Feature
2023-08-07 17:57:23 +00:00

144 lines
3.1 KiB
Go

package details
import (
"sync"
"github.com/alcionai/clues"
"golang.org/x/exp/maps"
"github.com/alcionai/corso/src/pkg/path"
)
// Builder should be used to create a details model.
type Builder struct {
d Details
mu sync.Mutex `json:"-"`
knownFolders map[string]Entry `json:"-"`
}
func (b *Builder) Add(
repoRef path.Path,
locationRef *path.Builder,
updated bool,
info ItemInfo,
) error {
b.mu.Lock()
defer b.mu.Unlock()
entry, err := b.d.add(
repoRef,
locationRef,
updated,
info)
if err != nil {
return clues.Wrap(err, "adding entry to details")
}
if err := b.addFolderEntries(
repoRef.ToBuilder().Dir(),
locationRef,
entry,
); err != nil {
return clues.Wrap(err, "adding folder entries")
}
return nil
}
func (b *Builder) addFolderEntries(
repoRef, locationRef *path.Builder,
entry Entry,
) error {
if len(repoRef.Elements()) < len(locationRef.Elements()) {
return clues.New("RepoRef shorter than LocationRef").
With("repo_ref", repoRef, "location_ref", locationRef)
}
if b.knownFolders == nil {
b.knownFolders = map[string]Entry{}
}
// Need a unique location because we want to have separate folders for
// different drives and categories even if there's duplicate folder names in
// them.
uniqueLoc, err := entry.uniqueLocation(locationRef)
if err != nil {
return clues.Wrap(err, "getting LocationIDer")
}
for uniqueLoc.elementCount() > 0 {
mapKey := uniqueLoc.ID().ShortRef()
name := uniqueLoc.lastElem()
if len(name) == 0 {
return clues.New("folder with no display name").
With("repo_ref", repoRef, "location_ref", uniqueLoc.InDetails())
}
shortRef := repoRef.ShortRef()
rr := repoRef.String()
// Get the parent of this entry to add as the LocationRef for the folder.
uniqueLoc.dir()
repoRef = repoRef.Dir()
parentRef := repoRef.ShortRef()
folder, ok := b.knownFolders[mapKey]
if !ok {
loc := uniqueLoc.InDetails().String()
folder = Entry{
RepoRef: rr,
ShortRef: shortRef,
ParentRef: parentRef,
LocationRef: loc,
ItemInfo: ItemInfo{
Folder: &FolderInfo{
ItemType: FolderItem,
// TODO(ashmrtn): Use the item type returned by the entry once
// SharePoint properly sets it.
DisplayName: name,
},
},
}
if err := entry.updateFolder(folder.Folder); err != nil {
return clues.Wrap(err, "adding folder").
With("parent_repo_ref", repoRef, "location_ref", loc)
}
}
folder.Folder.Size += entry.size()
folder.Updated = folder.Updated || entry.Updated
itemModified := entry.Modified()
if folder.Folder.Modified.Before(itemModified) {
folder.Folder.Modified = itemModified
}
// Always update the map because we're storing structs not pointers to
// structs.
b.knownFolders[mapKey] = folder
}
return nil
}
func (b *Builder) Details() *Details {
b.mu.Lock()
defer b.mu.Unlock()
ents := make([]Entry, len(b.d.Entries))
copy(ents, b.d.Entries)
// Write the cached folder entries to details
details := &Details{
DetailsModel{
Entries: append(ents, maps.Values(b.knownFolders)...),
},
}
return details
}