-
Notifications
You must be signed in to change notification settings - Fork 23
/
onedrive.go
559 lines (468 loc) · 17.7 KB
/
onedrive.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
package selectors
import (
"context"
"fmt"
"github.com/alcionai/clues"
"github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/backup/identity"
"github.com/alcionai/corso/src/pkg/dttm"
"github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/filters"
"github.com/alcionai/corso/src/pkg/path"
)
// ---------------------------------------------------------------------------
// Selectors
// ---------------------------------------------------------------------------
type (
// oneDrive provides an api for selecting
// data scopes applicable to the OneDrive service.
oneDrive struct {
Selector
}
// OneDriveBackup provides an api for selecting
// data scopes applicable to the OneDrive service,
// plus backup-specific methods.
OneDriveBackup struct {
oneDrive
}
// OneDriveRestorep provides an api for selecting
// data scopes applicable to the OneDrive service,
// plus restore-specific methods.
OneDriveRestore struct {
oneDrive
}
)
var (
_ Reducer = &OneDriveRestore{}
_ pathCategorier = &OneDriveRestore{}
_ reasoner = &OneDriveRestore{}
)
// NewOneDriveBackup produces a new Selector with the service set to ServiceOneDrive.
func NewOneDriveBackup(users []string) *OneDriveBackup {
src := OneDriveBackup{
oneDrive{
newSelector(ServiceOneDrive, users),
},
}
return &src
}
// ToOneDriveBackup transforms the generic selector into an OneDriveBackup.
// Errors if the service defined by the selector is not ServiceOneDrive.
func (s Selector) ToOneDriveBackup() (*OneDriveBackup, error) {
if s.Service != ServiceOneDrive {
return nil, badCastErr(ServiceOneDrive, s.Service)
}
src := OneDriveBackup{oneDrive{s}}
return &src, nil
}
func (s OneDriveBackup) SplitByResourceOwner(users []string) []OneDriveBackup {
sels := splitByProtectedResource[OneDriveScope](s.Selector, users, OneDriveUser)
ss := make([]OneDriveBackup, 0, len(sels))
for _, sel := range sels {
ss = append(ss, OneDriveBackup{oneDrive{sel}})
}
return ss
}
// NewOneDriveRestore produces a new Selector with the service set to ServiceOneDrive.
func NewOneDriveRestore(users []string) *OneDriveRestore {
src := OneDriveRestore{
oneDrive{
newSelector(ServiceOneDrive, users),
},
}
return &src
}
// ToOneDriveRestore transforms the generic selector into an OneDriveRestore.
// Errors if the service defined by the selector is not ServiceOneDrive.
func (s Selector) ToOneDriveRestore() (*OneDriveRestore, error) {
if s.Service != ServiceOneDrive {
return nil, badCastErr(ServiceOneDrive, s.Service)
}
src := OneDriveRestore{oneDrive{s}}
return &src, nil
}
func (s OneDriveRestore) SplitByResourceOwner(users []string) []OneDriveRestore {
sels := splitByProtectedResource[OneDriveScope](s.Selector, users, OneDriveUser)
ss := make([]OneDriveRestore, 0, len(sels))
for _, sel := range sels {
ss = append(ss, OneDriveRestore{oneDrive{sel}})
}
return ss
}
// PathCategories produces the aggregation of discrete users described by each type of scope.
func (s oneDrive) PathCategories() selectorPathCategories {
return selectorPathCategories{
Excludes: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Excludes),
Filters: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Filters),
Includes: pathCategoriesIn[OneDriveScope, oneDriveCategory](s.Includes),
}
}
// Reasons returns a deduplicated set of the backup reasons produced
// using the selector's discrete owner and each scopes' service and
// category types.
func (s oneDrive) Reasons(tenantID string, useOwnerNameForID bool) []identity.Reasoner {
return reasonsFor(s, tenantID, useOwnerNameForID)
}
// ---------------------------------------------------------------------------
// Stringers and Concealers
// ---------------------------------------------------------------------------
func (s OneDriveScope) Conceal() string { return conceal(s) }
func (s OneDriveScope) Format(fs fmt.State, r rune) { format(s, fs, r) }
func (s OneDriveScope) String() string { return conceal(s) }
func (s OneDriveScope) PlainString() string { return plainString(s) }
// -------------------
// Scope Factories
// Include appends the provided scopes to the selector's inclusion set.
// Data is included if it matches ANY inclusion.
// The inclusion set is later filtered (all included data must pass ALL
// filters) and excluded (all included data must not match ANY exclusion).
// Data is included if it matches ANY inclusion (of the same data category).
//
// All parts of the scope must match for data to be exclucded.
// Ex: File(u1, f1, m1) => only excludes a file if it is owned by user u1,
// located in folder f1, and ID'd as m1. Use selectors.Any() to wildcard
// a scope value. No value will match if selectors.None() is provided.
//
// Group-level scopes will automatically apply the Any() wildcard to
// child properties.
// ex: User(u1) automatically cascades to all folders and files owned
// by u1.
func (s *oneDrive) Include(scopes ...[]OneDriveScope) {
s.Includes = appendScopes(s.Includes, scopes...)
}
// Exclude appends the provided scopes to the selector's exclusion set.
// Every Exclusion scope applies globally, affecting all inclusion scopes.
// Data is excluded if it matches ANY exclusion.
//
// All parts of the scope must match for data to be exclucded.
// Ex: File(u1, f1, m1) => only excludes a file if it is owned by user u1,
// located in folder f1, and ID'd as m1. Use selectors.Any() to wildcard
// a scope value. No value will match if selectors.None() is provided.
//
// Group-level scopes will automatically apply the Any() wildcard to
// child properties.
// ex: User(u1) automatically cascades to all folders and files owned
// by u1.
func (s *oneDrive) Exclude(scopes ...[]OneDriveScope) {
s.Excludes = appendScopes(s.Excludes, scopes...)
}
// Filter appends the provided scopes to the selector's filters set.
// A selector with >0 filters and 0 inclusions will include any data
// that passes all filters.
// A selector with >0 filters and >0 inclusions will reduce the
// inclusion set to only the data that passes all filters.
// Data is retained if it passes ALL filters.
//
// All parts of the scope must match for data to be exclucded.
// Ex: File(u1, f1, m1) => only excludes a file if it is owned by user u1,
// located in folder f1, and ID'd as m1. Use selectors.Any() to wildcard
// a scope value. No value will match if selectors.None() is provided.
//
// Group-level scopes will automatically apply the Any() wildcard to
// child properties.
// ex: User(u1) automatically cascades to all folders and files owned
// by u1.
func (s *oneDrive) Filter(scopes ...[]OneDriveScope) {
s.Filters = appendScopes(s.Filters, scopes...)
}
// Scopes retrieves the list of oneDriveScopes in the selector.
func (s *oneDrive) Scopes() []OneDriveScope {
return scopes[OneDriveScope](s.Selector)
}
// -------------------
// Scope Factories
// Retrieves all OneDrive data.
// One scope is created per user entry.
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
// If any slice is empty, it defaults to [selectors.None]
func (s *oneDrive) AllData() []OneDriveScope {
scopes := []OneDriveScope{}
scopes = append(scopes, makeScope[OneDriveScope](OneDriveFolder, Any()))
return scopes
}
// Folders produces one or more OneDrive folder scopes.
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
// If any slice is empty, it defaults to [selectors.None]
// options are only applied to the folder scopes.
func (s *oneDrive) Folders(folders []string, opts ...option) []OneDriveScope {
var (
scopes = []OneDriveScope{}
os = append([]option{pathComparator()}, opts...)
)
scopes = append(
scopes,
makeScope[OneDriveScope](OneDriveFolder, folders, os...))
return scopes
}
// Items produces one or more OneDrive item scopes.
// If any slice contains selectors.Any, that slice is reduced to [selectors.Any]
// If any slice contains selectors.None, that slice is reduced to [selectors.None]
// If any slice is empty, it defaults to [selectors.None]
// options are only applied to the folder scopes.
func (s *oneDrive) Items(folders, items []string, opts ...option) []OneDriveScope {
scopes := []OneDriveScope{}
scopes = append(
scopes,
makeScope[OneDriveScope](OneDriveItem, items, defaultItemOptions(s.Cfg)...).
set(OneDriveFolder, folders, opts...))
return scopes
}
// -------------------
// Filter Factories
// CreatedAfter produces a OneDrive item created-after info scope.
// Matches any item where the created time is after the timestring.
// If the input equals selectors.Any, the scope will match all times.
// If the input is empty or selectors.None, the scope will always fail comparisons.
func (s *oneDrive) CreatedAfter(timeStrings string) []OneDriveScope {
return []OneDriveScope{
makeInfoScope[OneDriveScope](
OneDriveItem,
FileInfoCreatedAfter,
[]string{timeStrings},
filters.Less),
}
}
// CreatedBefore produces a OneDrive item created-before info scope.
// Matches any item where the created time is before the timestring.
// If the input equals selectors.Any, the scope will match all times.
// If the input is empty or selectors.None, the scope will always fail comparisons.
func (s *oneDrive) CreatedBefore(timeStrings string) []OneDriveScope {
return []OneDriveScope{
makeInfoScope[OneDriveScope](
OneDriveItem,
FileInfoCreatedBefore,
[]string{timeStrings},
filters.Greater),
}
}
// ModifiedAfter produces a OneDrive item modified-after info scope.
// Matches any item where the modified time is after the timestring.
// If the input equals selectors.Any, the scope will match all times.
// If the input is empty or selectors.None, the scope will always fail comparisons.
func (s *oneDrive) ModifiedAfter(timeStrings string) []OneDriveScope {
return []OneDriveScope{
makeInfoScope[OneDriveScope](
OneDriveItem,
FileInfoModifiedAfter,
[]string{timeStrings},
filters.Less),
}
}
// ModifiedBefore produces a OneDrive item modified-before info scope.
// Matches any item where the modified time is before the timestring.
// If the input equals selectors.Any, the scope will match all times.
// If the input is empty or selectors.None, the scope will always fail comparisons.
func (s *oneDrive) ModifiedBefore(timeStrings string) []OneDriveScope {
return []OneDriveScope{
makeInfoScope[OneDriveScope](
OneDriveItem,
FileInfoModifiedBefore,
[]string{timeStrings},
filters.Greater),
}
}
// ---------------------------------------------------------------------------
// Categories
// ---------------------------------------------------------------------------
// oneDriveCategory enumerates the type of the lowest level
// of data () in a scope.
type oneDriveCategory string
// interface compliance checks
var _ categorizer = OneDriveCategoryUnknown
const (
OneDriveCategoryUnknown oneDriveCategory = ""
// types of data in OneDrive
OneDriveUser oneDriveCategory = "OneDriveUser"
OneDriveItem oneDriveCategory = "OneDriveItem"
OneDriveFolder oneDriveCategory = "OneDriveFolder"
// details.ItemInfo comparables
FileInfoCreatedAfter oneDriveCategory = "FileInfoCreatedAfter"
FileInfoCreatedBefore oneDriveCategory = "FileInfoCreatedBefore"
FileInfoModifiedAfter oneDriveCategory = "FileInfoModifiedAfter"
FileInfoModifiedBefore oneDriveCategory = "FileInfoModifiedBefore"
)
// oneDriveLeafProperties describes common metadata of the leaf categories
var oneDriveLeafProperties = map[categorizer]leafProperty{
OneDriveItem: {
pathKeys: []categorizer{OneDriveFolder, OneDriveItem},
pathType: path.FilesCategory,
},
OneDriveUser: { // the root category must be represented, even though it isn't a leaf
pathKeys: []categorizer{OneDriveUser},
pathType: path.UnknownCategory,
},
}
func (c oneDriveCategory) String() string {
return string(c)
}
// leafCat returns the leaf category of the receiver.
// If the receiver category has multiple leaves (ex: User) or no leaves,
// (ex: Unknown), the receiver itself is returned.
// Ex: ServiceTypeFolder.leafCat() => ServiceTypeItem
// Ex: ServiceUser.leafCat() => ServiceUser
func (c oneDriveCategory) leafCat() categorizer {
switch c {
case OneDriveFolder, OneDriveItem,
FileInfoCreatedAfter, FileInfoCreatedBefore,
FileInfoModifiedAfter, FileInfoModifiedBefore:
return OneDriveItem
}
return c
}
// rootCat returns the root category type.
func (c oneDriveCategory) rootCat() categorizer {
return OneDriveUser
}
// unknownCat returns the unknown category type.
func (c oneDriveCategory) unknownCat() categorizer {
return OneDriveCategoryUnknown
}
// isUnion returns true if c is a user
func (c oneDriveCategory) isUnion() bool {
return c == c.rootCat()
}
// isLeaf is true if the category is a OneDriveItem category.
func (c oneDriveCategory) isLeaf() bool {
// return c == c.leafCat()??
return c == OneDriveItem
}
// pathValues transforms the two paths to maps of identified properties.
//
// Example:
// [tenantID, service, userPN, category, folder, fileID]
// => {odFolder: folder, odFileID: fileID}
func (c oneDriveCategory) pathValues(
repo path.Path,
ent details.Entry,
cfg Config,
) (map[categorizer][]string, error) {
if ent.OneDrive == nil {
return nil, clues.New("no OneDrive ItemInfo in details")
}
// Ignore `drives/<driveID>/root:` for folder comparison
rFld := ent.OneDrive.ParentPath
item := ent.ItemRef
if len(item) == 0 {
item = repo.Item()
}
if cfg.OnlyMatchItemNames {
item = ent.ItemInfo.OneDrive.ItemName
}
result := map[categorizer][]string{
OneDriveFolder: {rFld},
OneDriveItem: {item, ent.ShortRef},
}
if len(ent.LocationRef) > 0 {
result[OneDriveFolder] = append(result[OneDriveFolder], ent.LocationRef)
}
return result, nil
}
// pathKeys returns the path keys recognized by the receiver's leaf type.
func (c oneDriveCategory) pathKeys() []categorizer {
return oneDriveLeafProperties[c.leafCat()].pathKeys
}
// PathType converts the category's leaf type into the matching path.CategoryType.
func (c oneDriveCategory) PathType() path.CategoryType {
return oneDriveLeafProperties[c.leafCat()].pathType
}
// ---------------------------------------------------------------------------
// Scopes
// ---------------------------------------------------------------------------
// OneDriveScope specifies the data available
// when interfacing with the OneDrive service.
type OneDriveScope scope
// interface compliance checks
var _ scoper = &OneDriveScope{}
// Category describes the type of the data in scope.
func (s OneDriveScope) Category() oneDriveCategory {
return oneDriveCategory(getCategory(s))
}
// categorizer type is a generic wrapper around Category.
// Primarily used by scopes.go to for abstract comparisons.
func (s OneDriveScope) categorizer() categorizer {
return s.Category()
}
// InfoCategory returns the category enum of the scope info.
// If the scope is not an info type, returns OneDriveUnknownCategory.
func (s OneDriveScope) InfoCategory() oneDriveCategory {
return oneDriveCategory(getInfoCategory(s))
}
// IncludeCategory checks whether the scope includes a
// certain category of data.
// Ex: to check if the scope includes file data:
// s.IncludesCategory(selector.OneDriveFile)
func (s OneDriveScope) IncludesCategory(cat oneDriveCategory) bool {
return categoryMatches(s.Category(), cat)
}
// Matches returns true if the category is included in the scope's
// data type, and the target string matches that category's comparator.
func (s OneDriveScope) Matches(cat oneDriveCategory, target string) bool {
return matches(s, cat, target)
}
// returns true if the category is included in the scope's data type,
// and the value is set to Any().
func (s OneDriveScope) IsAny(cat oneDriveCategory) bool {
return IsAnyTarget(s, cat)
}
// Get returns the data category in the scope. If the scope
// contains all data types for a user, it'll return the
// OneDriveUser category.
func (s OneDriveScope) Get(cat oneDriveCategory) []string {
return getCatValue(s, cat)
}
// sets a value by category to the scope. Only intended for internal use.
func (s OneDriveScope) set(cat oneDriveCategory, v []string, opts ...option) OneDriveScope {
os := []option{}
if cat == OneDriveFolder {
os = append(os, pathComparator())
}
return set(s, cat, v, append(os, opts...)...)
}
// setDefaults ensures that user scopes express `AnyTgt` for their child category types.
func (s OneDriveScope) setDefaults() {
switch s.Category() {
case OneDriveUser:
s[OneDriveFolder.String()] = passAny
s[OneDriveItem.String()] = passAny
case OneDriveFolder:
s[OneDriveItem.String()] = passAny
}
}
// ---------------------------------------------------------------------------
// Backup Details Filtering
// ---------------------------------------------------------------------------
// Reduce filters the entries in a details struct to only those that match the
// inclusions, filters, and exclusions in the selector.
func (s oneDrive) Reduce(
ctx context.Context,
deets *details.Details,
errs *fault.Bus,
) *details.Details {
return reduce[OneDriveScope](
ctx,
deets,
s.Selector,
map[path.CategoryType]oneDriveCategory{
path.FilesCategory: OneDriveItem,
},
errs)
}
// matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo
// returns true if the scope and info match for the provided category.
func (s OneDriveScope) matchesInfo(dii details.ItemInfo) bool {
info := dii.OneDrive
if info == nil {
return false
}
infoCat := s.InfoCategory()
i := ""
switch infoCat {
case FileInfoCreatedAfter, FileInfoCreatedBefore:
i = dttm.Format(info.Created)
case FileInfoModifiedAfter, FileInfoModifiedBefore:
i = dttm.Format(info.Modified)
}
return s.Matches(infoCat, i)
}