corso/src/pkg/backup/details/details.go
ashmrtn 75c3e5cd33
Don't munge drive-based ShortRefs (#4009)
Munging code originally existed so that SDK
users would get unique ShortRefs if an item
moved or was renamed. However, we no longer
need to support that so this removes that
munging code

Manually tested making a new backup with this
PR where the merge base was a backup made prior
to this PR. A folder with a subfolder and items
was moved between the two backups. Backup
details for the new backup contained all the
expected entries

Restore selector logic should be unaffected
as that assumes the user has passed in a
ShortRef that was previously printed to the
CLI by `corso backup details`. Input is
compared against the ShortRef stored in the
entry, `ShortRef()` is not called on any `Path`

---

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

- [ ]  Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x]  No

#### Type of change

- [ ] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [x] 🧹 Tech Debt/Cleanup

#### Issue(s)

* closes #4012

#### Test Plan

- [x] 💪 Manual
- [x]  Unit test
- [ ] 💚 E2E
2023-08-16 17:37:30 +00:00

167 lines
4.3 KiB
Go

package details
import (
"encoding/json"
"io"
"strings"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/internal/m365/collection/drive/metadata"
"github.com/alcionai/corso/src/pkg/path"
)
// Max number of items for which we will print details. If there are
// more than this, then we just show a summary.
const maxPrintLimit = 50
// --------------------------------------------------------------------------------
// Details
// --------------------------------------------------------------------------------
// Details augments the core with a mutex for processing.
// Should be sliced back to d.DetailsModel for storage and
// printing.
type Details struct {
DetailsModel
}
func (d *Details) add(
repoRef path.Path,
locationRef *path.Builder,
info ItemInfo,
) (Entry, error) {
if locationRef == nil {
return Entry{}, clues.New("nil LocationRef").With("repo_ref", repoRef)
}
entry := Entry{
RepoRef: repoRef.String(),
ShortRef: repoRef.ShortRef(),
ParentRef: repoRef.ToBuilder().Dir().ShortRef(),
LocationRef: locationRef.String(),
ItemRef: repoRef.Item(),
ItemInfo: info,
}
// Use the item name and the path for the ShortRef. This ensures that renames
// within a directory generate unique ShortRefs.
if info.infoType() == OneDriveItem || info.infoType() == SharePointLibrary {
if info.OneDrive == nil && info.SharePoint == nil {
return entry, clues.New("item is not SharePoint or OneDrive type")
}
// clean metadata suffixes from item refs
entry.ItemRef = withoutMetadataSuffix(entry.ItemRef)
}
d.Entries = append(d.Entries, entry)
return entry, nil
}
// Marshal complies with the marshaller interface in streamStore.
func (d *Details) Marshal() ([]byte, error) {
return json.Marshal(d)
}
// UnmarshalTo produces a func that complies with the unmarshaller type in streamStore.
func UnmarshalTo(d *Details) func(io.ReadCloser) error {
return func(rc io.ReadCloser) error {
return json.NewDecoder(rc).Decode(d)
}
}
// remove metadata file suffixes from the string.
// assumes only one suffix is applied to any given id.
func withoutMetadataSuffix(id string) string {
id = strings.TrimSuffix(id, metadata.DirMetaFileSuffix)
id = strings.TrimSuffix(id, metadata.MetaFileSuffix)
id = strings.TrimSuffix(id, metadata.DataFileSuffix)
return id
}
// ---------------------------------------------------------------------------
// LocationIDer
// ---------------------------------------------------------------------------
// LocationIDer provides access to location information but guarantees that it
// can also generate a unique location (among items in the same service but
// possibly across data types within the service) that can be used as a key in
// maps and other structures. The unique location may be different than
// InDetails, the location used in backup details.
type LocationIDer interface {
ID() *path.Builder
InDetails() *path.Builder
}
type uniqueLoc struct {
pb *path.Builder
prefixElems int
}
func (ul uniqueLoc) ID() *path.Builder {
return ul.pb
}
func (ul uniqueLoc) InDetails() *path.Builder {
return path.Builder{}.Append(ul.pb.Elements()[ul.prefixElems:]...)
}
// elementCount returns the number of non-prefix elements in the LocationIDer
// (i.e. the number of elements in the InDetails path.Builder).
func (ul uniqueLoc) elementCount() int {
res := len(ul.pb.Elements()) - ul.prefixElems
if res < 0 {
res = 0
}
return res
}
func (ul *uniqueLoc) dir() {
if ul.elementCount() == 0 {
return
}
ul.pb = ul.pb.Dir()
}
// lastElem returns the unescaped last element in the location. If the location
// is empty returns an empty string.
func (ul uniqueLoc) lastElem() string {
if ul.elementCount() == 0 {
return ""
}
return ul.pb.LastElem()
}
// ---------------------------------------------------------------------------
// helpers
// ---------------------------------------------------------------------------
func updateFolderWithinDrive(
t ItemType,
driveName, driveID string,
f *FolderInfo,
) error {
if len(driveName) == 0 {
return clues.New("empty drive name")
} else if len(driveID) == 0 {
return clues.New("empty drive ID")
}
f.DriveName = driveName
f.DriveID = driveID
f.DataType = t
return nil
}
// ExtensionData stores extension data associated with an item
type ExtensionData struct {
Data map[string]any `json:"data,omitempty"`
}