Update item info api (#4183)
Update the API for Item.Info to return an error. This can then be leveraged to add lazy readers to exchange backups See #2023 for more info on how to add lazy readers to exchange --- #### 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) * #2023 #### Test Plan - [ ] 💪 Manual - [x] ⚡ Unit test - [x] 💚 E2E
This commit is contained in:
parent
039c2463fc
commit
1fe37e4ba9
@ -93,7 +93,7 @@ type PreviousLocationPather interface {
|
|||||||
|
|
||||||
// ItemInfo returns the details.ItemInfo for the item.
|
// ItemInfo returns the details.ItemInfo for the item.
|
||||||
type ItemInfo interface {
|
type ItemInfo interface {
|
||||||
Info() details.ItemInfo
|
Info() (details.ItemInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ItemSize returns the size of the item in bytes.
|
// ItemSize returns the size of the item in bytes.
|
||||||
|
|||||||
@ -17,7 +17,10 @@ import (
|
|||||||
// Item
|
// Item
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
var _ data.Item = &Item{}
|
var (
|
||||||
|
_ data.Item = &Item{}
|
||||||
|
_ data.ItemInfo = &Item{}
|
||||||
|
)
|
||||||
|
|
||||||
type Item struct {
|
type Item struct {
|
||||||
DeletedFlag bool
|
DeletedFlag bool
|
||||||
@ -45,8 +48,8 @@ func (s *Item) ToReader() io.ReadCloser {
|
|||||||
return s.Reader
|
return s.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Item) Info() details.ItemInfo {
|
func (s *Item) Info() (details.ItemInfo, error) {
|
||||||
return s.ItemInfo
|
return s.ItemInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Item) Size() int64 {
|
func (s *Item) Size() int64 {
|
||||||
|
|||||||
@ -132,7 +132,7 @@ func (rw *restoreStreamReader) Read(p []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type itemDetails struct {
|
type itemDetails struct {
|
||||||
infoFunc func() (details.ItemInfo, error)
|
infoer data.ItemInfo
|
||||||
repoPath path.Path
|
repoPath path.Path
|
||||||
prevPath path.Path
|
prevPath path.Path
|
||||||
locationPath *path.Builder
|
locationPath *path.Builder
|
||||||
@ -204,7 +204,7 @@ func (cp *corsoProgress) FinishedFile(relativePath string, err error) {
|
|||||||
|
|
||||||
// These items were sourced from a base snapshot or were cached in kopia so we
|
// These items were sourced from a base snapshot or were cached in kopia so we
|
||||||
// never had to materialize their details in-memory.
|
// never had to materialize their details in-memory.
|
||||||
if d.infoFunc == nil || d.cached {
|
if d.infoer == nil || d.cached {
|
||||||
if d.prevPath == nil {
|
if d.prevPath == nil {
|
||||||
cp.errs.AddRecoverable(ctx, clues.New("finished file sourced from previous backup with no previous path").
|
cp.errs.AddRecoverable(ctx, clues.New("finished file sourced from previous backup with no previous path").
|
||||||
WithClues(ctx).
|
WithClues(ctx).
|
||||||
@ -230,7 +230,7 @@ func (cp *corsoProgress) FinishedFile(relativePath string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
info, err := d.infoFunc()
|
info, err := d.infoer.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cp.errs.AddRecoverable(ctx, clues.Wrap(err, "getting ItemInfo").
|
cp.errs.AddRecoverable(ctx, clues.Wrap(err, "getting ItemInfo").
|
||||||
WithClues(ctx).
|
WithClues(ctx).
|
||||||
@ -412,11 +412,7 @@ func collectionEntries(
|
|||||||
// element. Add to pending set before calling the callback to avoid race
|
// element. Add to pending set before calling the callback to avoid race
|
||||||
// conditions when the item is completed.
|
// conditions when the item is completed.
|
||||||
d := &itemDetails{
|
d := &itemDetails{
|
||||||
// TODO(ashmrtn): Update API in data package to return an error and
|
infoer: ei,
|
||||||
// then remove this wrapper.
|
|
||||||
infoFunc: func() (details.ItemInfo, error) {
|
|
||||||
return ei.Info(), nil
|
|
||||||
},
|
|
||||||
repoPath: itemPath,
|
repoPath: itemPath,
|
||||||
// Also use the current path as the previous path for this item. This
|
// Also use the current path as the previous path for this item. This
|
||||||
// is so that if the item is marked as cached and we need to merge
|
// is so that if the item is marked as cached and we need to merge
|
||||||
|
|||||||
@ -373,6 +373,18 @@ func (suite *CorsoProgressUnitSuite) SetupSuite() {
|
|||||||
suite.targetFileName = suite.targetFilePath.ToBuilder().Dir().String()
|
suite.targetFileName = suite.targetFilePath.ToBuilder().Dir().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ data.ItemInfo = &mockExchangeMailInfoer{}
|
||||||
|
|
||||||
|
type mockExchangeMailInfoer struct{}
|
||||||
|
|
||||||
|
func (m mockExchangeMailInfoer) Info() (details.ItemInfo, error) {
|
||||||
|
return details.ItemInfo{
|
||||||
|
Exchange: &details.ExchangeInfo{
|
||||||
|
ItemType: details.ExchangeMail,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
type testInfo struct {
|
type testInfo struct {
|
||||||
info *itemDetails
|
info *itemDetails
|
||||||
err error
|
err error
|
||||||
@ -394,13 +406,7 @@ var finishedFileTable = []struct {
|
|||||||
return map[string]testInfo{
|
return map[string]testInfo{
|
||||||
fname: {
|
fname: {
|
||||||
info: &itemDetails{
|
info: &itemDetails{
|
||||||
infoFunc: func() (details.ItemInfo, error) {
|
infoer: mockExchangeMailInfoer{},
|
||||||
return details.ItemInfo{
|
|
||||||
Exchange: &details.ExchangeInfo{
|
|
||||||
ItemType: details.ExchangeMail,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
repoPath: fpath,
|
repoPath: fpath,
|
||||||
locationPath: path.Builder{}.Append(fpath.Folders()...),
|
locationPath: path.Builder{}.Append(fpath.Folders()...),
|
||||||
},
|
},
|
||||||
@ -432,13 +438,7 @@ var finishedFileTable = []struct {
|
|||||||
return map[string]testInfo{
|
return map[string]testInfo{
|
||||||
fname: {
|
fname: {
|
||||||
info: &itemDetails{
|
info: &itemDetails{
|
||||||
infoFunc: func() (details.ItemInfo, error) {
|
infoer: mockExchangeMailInfoer{},
|
||||||
return details.ItemInfo{
|
|
||||||
Exchange: &details.ExchangeInfo{
|
|
||||||
ItemType: details.ExchangeMail,
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
},
|
|
||||||
repoPath: fpath,
|
repoPath: fpath,
|
||||||
},
|
},
|
||||||
err: assert.AnError,
|
err: assert.AnError,
|
||||||
@ -530,7 +530,7 @@ func (suite *CorsoProgressUnitSuite) TestFinishedFile() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cachedTest.dropInfo {
|
if cachedTest.dropInfo {
|
||||||
v.info.infoFunc = nil
|
v.info.infoer = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -255,11 +255,11 @@ type Item struct {
|
|||||||
// Deleted implements an interface function. However, OneDrive items are marked
|
// Deleted implements an interface function. However, OneDrive items are marked
|
||||||
// as deleted by adding them to the exclude list so this can always return
|
// as deleted by adding them to the exclude list so this can always return
|
||||||
// false.
|
// false.
|
||||||
func (i Item) Deleted() bool { return false }
|
func (i Item) Deleted() bool { return false }
|
||||||
func (i *Item) ID() string { return i.id }
|
func (i *Item) ID() string { return i.id }
|
||||||
func (i *Item) ToReader() io.ReadCloser { return i.data }
|
func (i *Item) ToReader() io.ReadCloser { return i.data }
|
||||||
func (i *Item) Info() details.ItemInfo { return i.info }
|
func (i *Item) Info() (details.ItemInfo, error) { return i.info, nil }
|
||||||
func (i *Item) ModTime() time.Time { return i.info.Modified() }
|
func (i *Item) ModTime() time.Time { return i.info.Modified() }
|
||||||
|
|
||||||
// getDriveItemContent fetch drive item's contents with retries
|
// getDriveItemContent fetch drive item's contents with retries
|
||||||
func (oc *Collection) getDriveItemContent(
|
func (oc *Collection) getDriveItemContent(
|
||||||
|
|||||||
@ -980,7 +980,9 @@ func (suite *CollectionUnitTestSuite) TestItemExtensions() {
|
|||||||
|
|
||||||
ei, ok := collItem.(data.ItemInfo)
|
ei, ok := collItem.(data.ItemInfo)
|
||||||
assert.True(t, ok)
|
assert.True(t, ok)
|
||||||
itemInfo := ei.Info()
|
|
||||||
|
itemInfo, err := ei.Info()
|
||||||
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
_, err = io.ReadAll(collItem.ToReader())
|
_, err = io.ReadAll(collItem.ToReader())
|
||||||
test.expectReadErr(t, err, clues.ToCore(err))
|
test.expectReadErr(t, err, clues.ToCore(err))
|
||||||
|
|||||||
@ -320,8 +320,8 @@ func (i Item) Deleted() bool {
|
|||||||
return i.deleted
|
return i.deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) Info() details.ItemInfo {
|
func (i *Item) Info() (details.ItemInfo, error) {
|
||||||
return details.ItemInfo{Exchange: i.info}
|
return details.ItemInfo{Exchange: i.info}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) ModTime() time.Time {
|
func (i *Item) ModTime() time.Time {
|
||||||
|
|||||||
@ -168,8 +168,8 @@ func (i Item) Deleted() bool {
|
|||||||
return i.deleted
|
return i.deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) Info() details.ItemInfo {
|
func (i *Item) Info() (details.ItemInfo, error) {
|
||||||
return details.ItemInfo{Groups: i.info}
|
return details.ItemInfo{Groups: i.info}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Item) ModTime() time.Time {
|
func (i *Item) ModTime() time.Time {
|
||||||
|
|||||||
@ -149,8 +149,8 @@ func (sd Item) Deleted() bool {
|
|||||||
return sd.deleted
|
return sd.deleted
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *Item) Info() details.ItemInfo {
|
func (sd *Item) Info() (details.ItemInfo, error) {
|
||||||
return details.ItemInfo{SharePoint: sd.info}
|
return details.ItemInfo{SharePoint: sd.info}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *Item) ModTime() time.Time {
|
func (sd *Item) ModTime() time.Time {
|
||||||
|
|||||||
@ -183,9 +183,13 @@ func (suite *SharePointCollectionSuite) TestCollection_Items() {
|
|||||||
item := readItems[0]
|
item := readItems[0]
|
||||||
shareInfo, ok := item.(data.ItemInfo)
|
shareInfo, ok := item.(data.ItemInfo)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.NotNil(t, shareInfo.Info())
|
|
||||||
require.NotNil(t, shareInfo.Info().SharePoint)
|
info, err := shareInfo.Info()
|
||||||
assert.Equal(t, test.itemName, shareInfo.Info().SharePoint.ItemName)
|
require.NoError(t, err, clues.ToCore(err))
|
||||||
|
|
||||||
|
assert.NotNil(t, info)
|
||||||
|
assert.NotNil(t, info.SharePoint)
|
||||||
|
assert.Equal(t, test.itemName, info.SharePoint.ItemName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -732,16 +732,20 @@ func compareDriveItem(
|
|||||||
|
|
||||||
if !isMeta {
|
if !isMeta {
|
||||||
oitem := item.(*drive.Item)
|
oitem := item.(*drive.Item)
|
||||||
info := oitem.Info()
|
|
||||||
|
info, err := oitem.Info()
|
||||||
|
if !assert.NoError(t, err, clues.ToCore(err)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
if info.OneDrive != nil {
|
if info.OneDrive != nil {
|
||||||
displayName = oitem.Info().OneDrive.ItemName
|
displayName = info.OneDrive.ItemName
|
||||||
|
|
||||||
// Don't need to check SharePoint because it was added after we stopped
|
// Don't need to check SharePoint because it was added after we stopped
|
||||||
// adding meta files to backup details.
|
// adding meta files to backup details.
|
||||||
assert.False(t, oitem.Info().OneDrive.IsMeta, "meta marker for non meta item %s", name)
|
assert.False(t, info.OneDrive.IsMeta, "meta marker for non meta item %s", name)
|
||||||
} else if info.SharePoint != nil {
|
} else if info.SharePoint != nil {
|
||||||
displayName = oitem.Info().SharePoint.ItemName
|
displayName = info.SharePoint.ItemName
|
||||||
} else {
|
} else {
|
||||||
assert.Fail(t, "ItemInfo is not SharePoint or OneDrive")
|
assert.Fail(t, "ItemInfo is not SharePoint or OneDrive")
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user