From d763d9597628863b7919547dbedc36469c49dea7 Mon Sep 17 00:00:00 2001 From: Danny Date: Mon, 30 Jan 2023 11:51:32 -0500 Subject: [PATCH 01/46] BUG Fix: Restore: Operations (#2316) BUG FIX: Adds clause to return an error if experienced during path building of a collection. ## Description ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :bug: Bugfix - related to #2257 ## Test Plan - [x] :muscle: Manual --- src/internal/operations/restore.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index 3ed84c5f8..7dfd689d6 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -309,5 +309,9 @@ func formatDetailsForRestoration( paths[i] = p } + if errs != nil { + return nil, errs + } + return paths, nil } From ac1ff0c5cc686c302f211ac7e4bb8a63947bef35 Mon Sep 17 00:00:00 2001 From: Niraj Tolia Date: Mon, 30 Jan 2023 09:03:18 -0800 Subject: [PATCH 02/46] Add guidance on using Corso repos (#2315) ## Description Clarify how Corso repositories and object storage buckets are related. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :world_map: Documentation --- website/docs/setup/repos.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/website/docs/setup/repos.md b/website/docs/setup/repos.md index 6dbc62dff..3c2a7c119 100644 --- a/website/docs/setup/repos.md +++ b/website/docs/setup/repos.md @@ -10,9 +10,15 @@ import TabItem from '@theme/TabItem'; import TOCInline from '@theme/TOCInline'; import {Version} from '@site/src/corsoEnv'; -A Corso [repository](../concepts#corso-concepts) stores encrypted copies of your backup data. Corso uses +A Corso [repository](../concepts#corso-concepts) stores encrypted copies of a Microsoft 365 tenant's +backup data. Each repository is configured to store data in an object storage bucket and, optionally, +a user-specified prefix within the bucket. A repository is only meant to store a single tenant's data +but a single object storage bucket can contain multiple repositories if unique `--prefix` options are +specified when initializing a repository. + +Within a repository, Corso uses AES256-GCM-HMAC-SHA256 to encrypt data at rest using keys that are derived from the repository passphrase. -Data in flight is encrypted via TLS. +Data in flight to and from the repositiry is encrypted via TLS. Repositories are supported on the following object storage systems: From 637b1904aaea55bd39f6f35ded2e1ff2c7304ecc Mon Sep 17 00:00:00 2001 From: Keepers Date: Mon, 30 Jan 2023 11:57:45 -0700 Subject: [PATCH 03/46] remove operation "started" (#2247) ## Description This flag is confusing, and is better represented by tracking errors. ## Type of change - [x] :broom: Tech Debt/Cleanup ## Test Plan - [x] :zap: Unit test --- src/internal/operations/backup.go | 10 ++++----- src/internal/operations/backup_test.go | 8 +++----- src/internal/operations/restore.go | 27 +++++++++---------------- src/internal/operations/restore_test.go | 4 +--- 4 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 026061b59..7d6c14748 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -90,7 +90,6 @@ type backupStats struct { k *kopia.BackupStats gc *support.ConnectorOperationStatus resourceCount int - started bool readErr, writeErr error } @@ -228,7 +227,6 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { // should always be 1, since backups are 1:1 with resourceOwners. opStats.resourceCount = 1 - opStats.started = true return err } @@ -558,9 +556,12 @@ func (op *BackupOperation) persistResults( ) error { op.Results.StartedAt = started op.Results.CompletedAt = time.Now() + op.Results.ReadErrors = opStats.readErr + op.Results.WriteErrors = opStats.writeErr op.Status = Completed - if !opStats.started { + + if opStats.readErr != nil || opStats.writeErr != nil { op.Status = Failed return multierror.Append( @@ -573,9 +574,6 @@ func (op *BackupOperation) persistResults( op.Status = NoData } - op.Results.ReadErrors = opStats.readErr - op.Results.WriteErrors = opStats.writeErr - op.Results.BytesRead = opStats.k.TotalHashedBytes op.Results.BytesUploaded = opStats.k.TotalUploadedBytes op.Results.ItemsRead = opStats.gc.Successful diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index 795f341c6..2a7a1ecce 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -373,7 +373,6 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { expectStatus: Completed, expectErr: assert.NoError, stats: backupStats{ - started: true, resourceCount: 1, k: &kopia.BackupStats{ TotalFileCount: 1, @@ -389,7 +388,7 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { expectStatus: Failed, expectErr: assert.Error, stats: backupStats{ - started: false, + readErr: assert.AnError, k: &kopia.BackupStats{}, gc: &support.ConnectorOperationStatus{}, }, @@ -398,9 +397,8 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { expectStatus: NoData, expectErr: assert.NoError, stats: backupStats{ - started: true, - k: &kopia.BackupStats{}, - gc: &support.ConnectorOperationStatus{}, + k: &kopia.BackupStats{}, + gc: &support.ConnectorOperationStatus{}, }, }, } diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index 7dfd689d6..8ddaa8d29 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -89,7 +89,6 @@ type restoreStats struct { gc *support.ConnectorOperationStatus bytesRead *stats.ByteCounter resourceCount int - started bool readErr, writeErr error // a transient value only used to pair up start-end events. @@ -143,10 +142,8 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De detailsStore, ) if err != nil { - err = errors.Wrap(err, "restore") - opStats.readErr = err - - return nil, err + opStats.readErr = errors.Wrap(err, "restore") + return nil, opStats.readErr } ctx = clues.Add(ctx, "resource_owner", bup.Selector.DiscreteOwner) @@ -178,10 +175,8 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De dcs, err := op.kopia.RestoreMultipleItems(ctx, bup.SnapshotID, paths, opStats.bytesRead) if err != nil { - err = errors.Wrap(err, "retrieving service data") - opStats.readErr = err - - return nil, err + opStats.readErr = errors.Wrap(err, "retrieving service data") + return nil, opStats.readErr } kopiaComplete <- struct{}{} @@ -207,14 +202,11 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De op.Destination, dcs) if err != nil { - err = errors.Wrap(err, "restoring service data") - opStats.writeErr = err - - return nil, err + opStats.writeErr = errors.Wrap(err, "restoring service data") + return nil, opStats.writeErr } restoreComplete <- struct{}{} - opStats.started = true opStats.gc = gc.AwaitStatus() logger.Ctx(ctx).Debug(gc.PrintableStatus()) @@ -230,10 +222,12 @@ func (op *RestoreOperation) persistResults( ) error { op.Results.StartedAt = started op.Results.CompletedAt = time.Now() + op.Results.ReadErrors = opStats.readErr + op.Results.WriteErrors = opStats.writeErr op.Status = Completed - if !opStats.started { + if opStats.readErr != nil || opStats.writeErr != nil { op.Status = Failed return multierror.Append( @@ -246,9 +240,6 @@ func (op *RestoreOperation) persistResults( op.Status = NoData } - op.Results.ReadErrors = opStats.readErr - op.Results.WriteErrors = opStats.writeErr - op.Results.BytesRead = opStats.bytesRead.NumBytes op.Results.ItemsRead = len(opStats.cs) // TODO: file count, not collection count op.Results.ItemsWritten = opStats.gc.Successful diff --git a/src/internal/operations/restore_test.go b/src/internal/operations/restore_test.go index 44de04c66..973a178b4 100644 --- a/src/internal/operations/restore_test.go +++ b/src/internal/operations/restore_test.go @@ -57,7 +57,6 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { expectStatus: Completed, expectErr: assert.NoError, stats: restoreStats{ - started: true, resourceCount: 1, bytesRead: &stats.ByteCounter{ NumBytes: 42, @@ -73,7 +72,7 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { expectStatus: Failed, expectErr: assert.Error, stats: restoreStats{ - started: false, + readErr: assert.AnError, bytesRead: &stats.ByteCounter{}, gc: &support.ConnectorOperationStatus{}, }, @@ -82,7 +81,6 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { expectStatus: NoData, expectErr: assert.NoError, stats: restoreStats{ - started: true, bytesRead: &stats.ByteCounter{}, cs: []data.Collection{}, gc: &support.ConnectorOperationStatus{}, From 03642c517f69862d82d00851dfffbf029ac192c3 Mon Sep 17 00:00:00 2001 From: Keepers Date: Mon, 30 Jan 2023 12:24:44 -0700 Subject: [PATCH 04/46] standardize deleteContainer func in exch api (#2246) ## Description Minor refactor that came up while hunting bugs. For the life of me, I cannot find the reason for read/write counts being off in event incrementals. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2022 ## Test Plan - [x] :muscle: Manual - [x] :green_heart: E2E --- .../connector/exchange/api/contacts.go | 5 +- src/internal/connector/exchange/api/events.go | 4 +- src/internal/connector/exchange/api/mail.go | 4 +- .../connector/exchange/restore_test.go | 75 +++++++++---------- src/internal/kopia/upload.go | 16 ++++ .../operations/backup_integration_test.go | 21 +++--- src/internal/tester/storage.go | 5 +- 7 files changed, 74 insertions(+), 56 deletions(-) diff --git a/src/internal/connector/exchange/api/contacts.go b/src/internal/connector/exchange/api/contacts.go index 2e9014235..0db1e964c 100644 --- a/src/internal/connector/exchange/api/contacts.go +++ b/src/internal/connector/exchange/api/contacts.go @@ -48,9 +48,8 @@ func (c Contacts) CreateContactFolder( return c.stable.Client().UsersById(user).ContactFolders().Post(ctx, requestBody, nil) } -// DeleteContactFolder deletes the ContactFolder associated with the M365 ID if permissions are valid. -// Errors returned if the function call was not successful. -func (c Contacts) DeleteContactFolder( +// DeleteContainer deletes the ContactFolder associated with the M365 ID if permissions are valid. +func (c Contacts) DeleteContainer( ctx context.Context, user, folderID string, ) error { diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index b3e6f9467..e643c1f89 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -50,9 +50,9 @@ func (c Events) CreateCalendar( return c.stable.Client().UsersById(user).Calendars().Post(ctx, requestbody, nil) } -// DeleteCalendar removes calendar from user's M365 account +// DeleteContainer removes a calendar from user's M365 account // Reference: https://docs.microsoft.com/en-us/graph/api/calendar-delete?view=graph-rest-1.0&tabs=go -func (c Events) DeleteCalendar( +func (c Events) DeleteContainer( ctx context.Context, user, calendarID string, ) error { diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index 4c9d564f0..bbac48a66 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -72,9 +72,9 @@ func (c Mail) CreateMailFolderWithParent( Post(ctx, requestBody, nil) } -// DeleteMailFolder removes a mail folder with the corresponding M365 ID from the user's M365 Exchange account +// DeleteContainer removes a mail folder with the corresponding M365 ID from the user's M365 Exchange account // Reference: https://docs.microsoft.com/en-us/graph/api/mailfolder-delete?view=graph-rest-1.0&tabs=http -func (c Mail) DeleteMailFolder( +func (c Mail) DeleteContainer( ctx context.Context, user, folderID string, ) error { diff --git a/src/internal/connector/exchange/restore_test.go b/src/internal/connector/exchange/restore_test.go index f439ea3ad..9c32fd530 100644 --- a/src/internal/connector/exchange/restore_test.go +++ b/src/internal/connector/exchange/restore_test.go @@ -76,7 +76,7 @@ func (suite *ExchangeRestoreSuite) TestRestoreContact() { defer func() { // Remove the folder containing contact prior to exiting test - err = suite.ac.Contacts().DeleteContactFolder(ctx, userID, folderID) + err = suite.ac.Contacts().DeleteContainer(ctx, userID, folderID) assert.NoError(t, err) }() @@ -110,7 +110,7 @@ func (suite *ExchangeRestoreSuite) TestRestoreEvent() { defer func() { // Removes calendar containing events created during the test - err = suite.ac.Events().DeleteCalendar(ctx, userID, calendarID) + err = suite.ac.Events().DeleteContainer(ctx, userID, calendarID) assert.NoError(t, err) }() @@ -124,6 +124,10 @@ func (suite *ExchangeRestoreSuite) TestRestoreEvent() { assert.NotNil(t, info, "event item info") } +type containerDeleter interface { + DeleteContainer(context.Context, string, string) error +} + // TestRestoreExchangeObject verifies path.Category usage for restored objects func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { a := tester.NewM365Account(suite.T()) @@ -133,20 +137,24 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { service, err := createService(m365) require.NoError(suite.T(), err) + deleters := map[path.CategoryType]containerDeleter{ + path.EmailCategory: suite.ac.Mail(), + path.ContactsCategory: suite.ac.Contacts(), + path.EventsCategory: suite.ac.Events(), + } + userID := tester.M365UserID(suite.T()) now := time.Now() tests := []struct { name string bytes []byte category path.CategoryType - cleanupFunc func(context.Context, string, string) error destination func(*testing.T, context.Context) string }{ { - name: "Test Mail", - bytes: mockconnector.GetMockMessageBytes("Restore Exchange Object"), - category: path.EmailCategory, - cleanupFunc: suite.ac.Mail().DeleteMailFolder, + name: "Test Mail", + bytes: mockconnector.GetMockMessageBytes("Restore Exchange Object"), + category: path.EmailCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreMailObject: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) @@ -156,10 +164,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Mail: One Direct Attachment", - bytes: mockconnector.GetMockMessageWithDirectAttachment("Restore 1 Attachment"), - category: path.EmailCategory, - cleanupFunc: suite.ac.Mail().DeleteMailFolder, + name: "Test Mail: One Direct Attachment", + bytes: mockconnector.GetMockMessageWithDirectAttachment("Restore 1 Attachment"), + category: path.EmailCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreMailwithAttachment: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) @@ -169,10 +176,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Mail: One Large Attachment", - bytes: mockconnector.GetMockMessageWithLargeAttachment("Restore Large Attachment"), - category: path.EmailCategory, - cleanupFunc: suite.ac.Mail().DeleteMailFolder, + name: "Test Mail: One Large Attachment", + bytes: mockconnector.GetMockMessageWithLargeAttachment("Restore Large Attachment"), + category: path.EmailCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreMailwithLargeAttachment: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) @@ -182,10 +188,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Mail: Two Attachments", - bytes: mockconnector.GetMockMessageWithTwoAttachments("Restore 2 Attachments"), - category: path.EmailCategory, - cleanupFunc: suite.ac.Mail().DeleteMailFolder, + name: "Test Mail: Two Attachments", + bytes: mockconnector.GetMockMessageWithTwoAttachments("Restore 2 Attachments"), + category: path.EmailCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreMailwithAttachments: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) @@ -195,10 +200,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Mail: Reference(OneDrive) Attachment", - bytes: mockconnector.GetMessageWithOneDriveAttachment("Restore Reference(OneDrive) Attachment"), - category: path.EmailCategory, - cleanupFunc: suite.ac.Mail().DeleteMailFolder, + name: "Test Mail: Reference(OneDrive) Attachment", + bytes: mockconnector.GetMessageWithOneDriveAttachment("Restore Reference(OneDrive) Attachment"), + category: path.EmailCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreMailwithReferenceAttachment: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) @@ -209,10 +213,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, // TODO: #884 - reinstate when able to specify root folder by name { - name: "Test Contact", - bytes: mockconnector.GetMockContactBytes("Test_Omega"), - category: path.ContactsCategory, - cleanupFunc: suite.ac.Contacts().DeleteContactFolder, + name: "Test Contact", + bytes: mockconnector.GetMockContactBytes("Test_Omega"), + category: path.ContactsCategory, destination: func(t *testing.T, ctx context.Context) string { folderName := "TestRestoreContactObject: " + common.FormatSimpleDateTime(now) folder, err := suite.ac.Contacts().CreateContactFolder(ctx, userID, folderName) @@ -222,10 +225,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Events", - bytes: mockconnector.GetDefaultMockEventBytes("Restored Event Object"), - category: path.EventsCategory, - cleanupFunc: suite.ac.Events().DeleteCalendar, + name: "Test Events", + bytes: mockconnector.GetDefaultMockEventBytes("Restored Event Object"), + category: path.EventsCategory, destination: func(t *testing.T, ctx context.Context) string { calendarName := "TestRestoreEventObject: " + common.FormatSimpleDateTime(now) calendar, err := suite.ac.Events().CreateCalendar(ctx, userID, calendarName) @@ -235,10 +237,9 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { }, }, { - name: "Test Event with Attachment", - bytes: mockconnector.GetMockEventWithAttachment("Restored Event Attachment"), - category: path.EventsCategory, - cleanupFunc: suite.ac.Events().DeleteCalendar, + name: "Test Event with Attachment", + bytes: mockconnector.GetMockEventWithAttachment("Restored Event Attachment"), + category: path.EventsCategory, destination: func(t *testing.T, ctx context.Context) string { calendarName := "TestRestoreEventObject_" + common.FormatSimpleDateTime(now) calendar, err := suite.ac.Events().CreateCalendar(ctx, userID, calendarName) @@ -266,9 +267,7 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { ) assert.NoError(t, err, support.ConnectorStackErrorTrace(err)) assert.NotNil(t, info, "item info is populated") - - cleanupError := test.cleanupFunc(ctx, userID, destination) - assert.NoError(t, cleanupError) + assert.NoError(t, deleters[test.category].DeleteContainer(ctx, userID, destination)) }) } } diff --git a/src/internal/kopia/upload.go b/src/internal/kopia/upload.go index 5301e6872..8ddb46978 100644 --- a/src/internal/kopia/upload.go +++ b/src/internal/kopia/upload.go @@ -3,10 +3,13 @@ package kopia import ( "bytes" "context" + "encoding/base64" "encoding/binary" + "fmt" "io" "os" "runtime/trace" + "strings" "sync" "sync/atomic" "time" @@ -204,6 +207,19 @@ func (cp *corsoProgress) FinishedHashingFile(fname string, bs int64) { // Pass the call through as well so we don't break expected functionality. defer cp.UploadProgress.FinishedHashingFile(fname, bs) + sl := strings.Split(fname, "/") + + for i := range sl { + rdt, err := base64.StdEncoding.DecodeString(sl[i]) + if err != nil { + fmt.Println("f did not decode") + } + + sl[i] = string(rdt) + } + + logger.Ctx(context.Background()).Debugw("finished hashing file", "path", sl[2:]) + atomic.AddInt64(&cp.totalBytes, bs) } diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index f6e63b022..c28159c1a 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -633,6 +633,8 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { ctx, flush := tester.NewContext() defer flush() + tester.LogTimeOfTest(suite.T()) + var ( t = suite.T() acct = tester.NewM365Account(t) @@ -803,7 +805,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { { name: "move an email folder to a subfolder", updateUserData: func(t *testing.T) { - // contacts cannot be sufoldered; this is an email-only change + // contacts and events cannot be sufoldered; this is an email-only change toContainer := dataset[path.EmailCategory].dests[container1].containerID fromContainer := dataset[path.EmailCategory].dests[container2].containerID @@ -826,23 +828,22 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { updateUserData: func(t *testing.T) { for category, d := range dataset { containerID := d.dests[container2].containerID - cli := gc.Service.Client().UsersById(suite.user) switch category { case path.EmailCategory: require.NoError( t, - cli.MailFoldersById(containerID).Delete(ctx, nil), + ac.Mail().DeleteContainer(ctx, suite.user, containerID), "deleting an email folder") case path.ContactsCategory: require.NoError( t, - cli.ContactFoldersById(containerID).Delete(ctx, nil), + ac.Contacts().DeleteContainer(ctx, suite.user, containerID), "deleting a contacts folder") case path.EventsCategory: require.NoError( t, - cli.CalendarsById(containerID).Delete(ctx, nil), + ac.Events().DeleteContainer(ctx, suite.user, containerID), "deleting a calendar") } } @@ -923,19 +924,19 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { require.NoError(t, err, "updating contact folder name") case path.EventsCategory: - ccf := cli.CalendarsById(containerID) + cbi := cli.CalendarsById(containerID) - body, err := ccf.Get(ctx, nil) + body, err := cbi.Get(ctx, nil) require.NoError(t, err, "getting calendar") body.SetName(&containerRename) - _, err = ccf.Patch(ctx, body, nil) + _, err = cbi.Patch(ctx, body, nil) require.NoError(t, err, "updating calendar name") } } }, - itemsRead: 0, - itemsWritten: 4, + itemsRead: 0, // containers are not counted as reads + itemsWritten: 4, // two items per category }, { name: "add a new item", diff --git a/src/internal/tester/storage.go b/src/internal/tester/storage.go index 0cfabba20..3e16ce05e 100644 --- a/src/internal/tester/storage.go +++ b/src/internal/tester/storage.go @@ -29,11 +29,14 @@ func NewPrefixedS3Storage(t *testing.T) storage.Storage { cfg, err := readTestConfig() require.NoError(t, err, "configuring storage from test file") + prefix := testRepoRootPrefix + t.Name() + "-" + now + t.Logf("testing at s3 bucket [%s] prefix [%s]", cfg[TestCfgBucket], prefix) + st, err := storage.NewStorage( storage.ProviderS3, storage.S3Config{ Bucket: cfg[TestCfgBucket], - Prefix: testRepoRootPrefix + t.Name() + "-" + now, + Prefix: prefix, }, storage.CommonConfig{ Corso: credentials.GetCorso(), From 2458508969c05e6e6b099fc470c227d35d8a262c Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Mon, 30 Jan 2023 11:44:06 -0800 Subject: [PATCH 05/46] Wire up GraphConnector <-> BackupOp item exclude list (#2245) ## Description Return an item exclude list from GraphConnector to BackupOp. BackupOp does not yet pass this to kopia wrapper. Returned list is set to nil (eventually) by all components so even if this were wired to kopia wrapper it wouldn't change the current behavior of the system ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2243 merge after: * #2143 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- src/internal/connector/data_collections.go | 36 ++++++++++--------- .../connector/data_collections_test.go | 19 +++++++--- .../connector/exchange/data_collections.go | 12 ++++--- .../connector/graph_connector_test.go | 8 +++-- .../connector/onedrive/collections.go | 14 ++++---- src/internal/connector/onedrive/drive_test.go | 4 ++- .../connector/sharepoint/data_collections.go | 20 +++++------ src/internal/operations/backup.go | 6 +++- 8 files changed, 73 insertions(+), 46 deletions(-) diff --git a/src/internal/connector/data_collections.go b/src/internal/connector/data_collections.go index 0b6d20b27..7d187a854 100644 --- a/src/internal/connector/data_collections.go +++ b/src/internal/connector/data_collections.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/pkg/errors" + "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/connector/discovery" "github.com/alcionai/corso/src/internal/connector/discovery/api" @@ -35,27 +36,27 @@ func (gc *GraphConnector) DataCollections( sels selectors.Selector, metadata []data.Collection, ctrlOpts control.Options, -) ([]data.Collection, error) { +) ([]data.Collection, map[string]struct{}, error) { ctx, end := D.Span(ctx, "gc:dataCollections", D.Index("service", sels.Service.String())) defer end() err := verifyBackupInputs(sels, gc.GetUsers(), gc.GetSiteIDs()) if err != nil { - return nil, err + return nil, nil, err } serviceEnabled, err := checkServiceEnabled(ctx, gc.Owners.Users(), path.ServiceType(sels.Service), sels.DiscreteOwner) if err != nil { - return nil, err + return nil, nil, err } if !serviceEnabled { - return []data.Collection{}, nil + return []data.Collection{}, nil, nil } switch sels.Service { case selectors.ServiceExchange: - colls, err := exchange.DataCollections( + colls, excludes, err := exchange.DataCollections( ctx, sels, metadata, @@ -64,7 +65,7 @@ func (gc *GraphConnector) DataCollections( gc.UpdateStatus, ctrlOpts) if err != nil { - return nil, err + return nil, nil, err } for _, c := range colls { @@ -79,13 +80,13 @@ func (gc *GraphConnector) DataCollections( } } - return colls, nil + return colls, excludes, nil case selectors.ServiceOneDrive: return gc.OneDriveDataCollections(ctx, sels, ctrlOpts) case selectors.ServiceSharePoint: - colls, err := sharepoint.DataCollections( + colls, excludes, err := sharepoint.DataCollections( ctx, gc.itemClient, sels, @@ -94,17 +95,17 @@ func (gc *GraphConnector) DataCollections( gc, ctrlOpts) if err != nil { - return nil, err + return nil, nil, err } for range colls { gc.incrementAwaitingMessages() } - return colls, nil + return colls, excludes, nil default: - return nil, errors.Errorf("service %s not supported", sels.Service.String()) + return nil, nil, errors.Errorf("service %s not supported", sels.Service.String()) } } @@ -182,15 +183,16 @@ func (gc *GraphConnector) OneDriveDataCollections( ctx context.Context, selector selectors.Selector, ctrlOpts control.Options, -) ([]data.Collection, error) { +) ([]data.Collection, map[string]struct{}, error) { odb, err := selector.ToOneDriveBackup() if err != nil { - return nil, errors.Wrap(err, "oneDriveDataCollection: parsing selector") + return nil, nil, errors.Wrap(err, "oneDriveDataCollection: parsing selector") } var ( user = selector.DiscreteOwner collections = []data.Collection{} + allExcludes = map[string]struct{}{} errs error ) @@ -198,7 +200,7 @@ func (gc *GraphConnector) OneDriveDataCollections( for _, scope := range odb.Scopes() { logger.Ctx(ctx).With("user", user).Debug("Creating OneDrive collections") - odcs, err := onedrive.NewCollections( + odcs, excludes, err := onedrive.NewCollections( gc.itemClient, gc.credentials.AzureTenantID, user, @@ -209,15 +211,17 @@ func (gc *GraphConnector) OneDriveDataCollections( ctrlOpts, ).Get(ctx) if err != nil { - return nil, support.WrapAndAppend(user, err, errs) + return nil, nil, support.WrapAndAppend(user, err, errs) } collections = append(collections, odcs...) + + maps.Copy(allExcludes, excludes) } for range collections { gc.incrementAwaitingMessages() } - return collections, errs + return collections, allExcludes, errs } diff --git a/src/internal/connector/data_collections_test.go b/src/internal/connector/data_collections_test.go index 877975bb5..c90bee511 100644 --- a/src/internal/connector/data_collections_test.go +++ b/src/internal/connector/data_collections_test.go @@ -99,7 +99,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { - collections, err := exchange.DataCollections( + collections, excludes, err := exchange.DataCollections( ctx, test.getSelector(t), nil, @@ -108,6 +108,8 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestExchangeDataCollection control.Options{}) require.NoError(t, err) + assert.Empty(t, excludes) + for range collections { connector.incrementAwaitingMessages() } @@ -199,9 +201,10 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestDataCollections_invali for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { - collections, err := connector.DataCollections(ctx, test.getSelector(t), nil, control.Options{}) + collections, excludes, err := connector.DataCollections(ctx, test.getSelector(t), nil, control.Options{}) assert.Error(t, err) assert.Empty(t, collections) + assert.Empty(t, excludes) }) } } @@ -242,7 +245,7 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestSharePointDataCollecti for _, test := range tests { suite.T().Run(test.name, func(t *testing.T) { - collections, err := sharepoint.DataCollections( + collections, excludes, err := sharepoint.DataCollections( ctx, graph.HTTPClient(graph.NoTimeout()), test.getSelector(), @@ -251,6 +254,8 @@ func (suite *ConnectorDataCollectionIntegrationSuite) TestSharePointDataCollecti connector, control.Options{}) require.NoError(t, err) + // Not expecting excludes as this isn't an incremental backup. + assert.Empty(t, excludes) for range collections { connector.incrementAwaitingMessages() @@ -320,9 +325,11 @@ func (suite *ConnectorCreateSharePointCollectionIntegrationSuite) TestCreateShar sel := selectors.NewSharePointBackup(siteIDs) sel.Include(sel.Libraries([]string{"foo"}, selectors.PrefixMatch())) - cols, err := gc.DataCollections(ctx, sel.Selector, nil, control.Options{}) + cols, excludes, err := gc.DataCollections(ctx, sel.Selector, nil, control.Options{}) require.NoError(t, err) assert.Len(t, cols, 1) + // No excludes yet as this isn't an incremental backup. + assert.Empty(t, excludes) for _, collection := range cols { t.Logf("Path: %s\n", collection.FullPath().String()) @@ -344,9 +351,11 @@ func (suite *ConnectorCreateSharePointCollectionIntegrationSuite) TestCreateShar sel := selectors.NewSharePointBackup(siteIDs) sel.Include(sel.Lists(selectors.Any(), selectors.PrefixMatch())) - cols, err := gc.DataCollections(ctx, sel.Selector, nil, control.Options{}) + cols, excludes, err := gc.DataCollections(ctx, sel.Selector, nil, control.Options{}) require.NoError(t, err) assert.Less(t, 0, len(cols)) + // No excludes yet as this isn't an incremental backup. + assert.Empty(t, excludes) for _, collection := range cols { t.Logf("Path: %s\n", collection.FullPath().String()) diff --git a/src/internal/connector/exchange/data_collections.go b/src/internal/connector/exchange/data_collections.go index 619d75cee..41bc16301 100644 --- a/src/internal/connector/exchange/data_collections.go +++ b/src/internal/connector/exchange/data_collections.go @@ -167,10 +167,10 @@ func DataCollections( acct account.M365Config, su support.StatusUpdater, ctrlOpts control.Options, -) ([]data.Collection, error) { +) ([]data.Collection, map[string]struct{}, error) { eb, err := selector.ToExchangeBackup() if err != nil { - return nil, errors.Wrap(err, "exchangeDataCollection: parsing selector") + return nil, nil, errors.Wrap(err, "exchangeDataCollection: parsing selector") } var ( @@ -181,7 +181,7 @@ func DataCollections( cdps, err := parseMetadataCollections(ctx, metadata) if err != nil { - return nil, err + return nil, nil, err } for _, scope := range eb.Scopes() { @@ -196,13 +196,15 @@ func DataCollections( ctrlOpts, su) if err != nil { - return nil, support.WrapAndAppend(user, err, errs) + return nil, nil, support.WrapAndAppend(user, err, errs) } collections = append(collections, dcs...) } - return collections, errs + // Exchange does not require adding items to the global exclude list so always + // return nil. + return collections, nil, errs } func getterByType(ac api.Client, category path.CategoryType) (addedAndRemovedItemIDsGetter, error) { diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 1ed353be6..be1439c35 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -425,8 +425,10 @@ func runRestoreBackupTest( t.Logf("Selective backup of %s\n", backupSel) start = time.Now() - dcs, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) + dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) require.NoError(t, err) + // No excludes yet because this isn't an incremental backup. + assert.Empty(t, excludes) t.Logf("Backup enumeration complete in %v\n", time.Since(start)) @@ -898,8 +900,10 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames backupSel := backupSelectorForExpected(t, test.service, expectedDests) t.Log("Selective backup of", backupSel) - dcs, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) + dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) require.NoError(t, err) + // No excludes yet because this isn't an incremental backup. + assert.Empty(t, excludes) t.Log("Backup enumeration complete") diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index 0813c13ed..f83ce342a 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -92,19 +92,20 @@ func NewCollections( } } -// Retrieves drive data as set of `data.Collections` -func (c *Collections) Get(ctx context.Context) ([]data.Collection, error) { +// Retrieves drive data as set of `data.Collections` and a set of item names to +// be excluded from the upcoming backup. +func (c *Collections) Get(ctx context.Context) ([]data.Collection, map[string]struct{}, error) { // Enumerate drives for the specified resourceOwner pager, err := PagerForSource(c.source, c.service, c.resourceOwner, nil) if err != nil { - return nil, err + return nil, nil, err } retry := c.source == OneDriveSource drives, err := drives(ctx, pager, retry) if err != nil { - return nil, err + return nil, nil, err } var ( @@ -133,7 +134,7 @@ func (c *Collections) Get(ctx context.Context) ([]data.Collection, error) { c.UpdateCollections, ) if err != nil { - return nil, err + return nil, nil, err } if len(delta) > 0 { @@ -185,7 +186,8 @@ func (c *Collections) Get(ctx context.Context) ([]data.Collection, error) { collections = append(collections, metadata) } - return collections, nil + // TODO(ashmrtn): Track and return the set of items to exclude. + return collections, nil, nil } // UpdateCollections initializes and adds the provided drive items to Collections diff --git a/src/internal/connector/onedrive/drive_test.go b/src/internal/connector/onedrive/drive_test.go index 66449a917..36fef30ab 100644 --- a/src/internal/connector/onedrive/drive_test.go +++ b/src/internal/connector/onedrive/drive_test.go @@ -454,7 +454,7 @@ func (suite *OneDriveSuite) TestOneDriveNewCollections() { scope := selectors. NewOneDriveBackup([]string{test.user}). AllData()[0] - odcs, err := NewCollections( + odcs, excludes, err := NewCollections( graph.HTTPClient(graph.NoTimeout()), creds.AzureTenantID, test.user, @@ -465,6 +465,8 @@ func (suite *OneDriveSuite) TestOneDriveNewCollections() { control.Options{}, ).Get(ctx) assert.NoError(t, err) + // Don't expect excludes as this isn't an incremental backup. + assert.Empty(t, excludes) for _, entry := range odcs { assert.NotEmpty(t, entry.FullPath()) diff --git a/src/internal/connector/sharepoint/data_collections.go b/src/internal/connector/sharepoint/data_collections.go index d64542271..6011c32a0 100644 --- a/src/internal/connector/sharepoint/data_collections.go +++ b/src/internal/connector/sharepoint/data_collections.go @@ -31,10 +31,10 @@ func DataCollections( serv graph.Servicer, su statusUpdater, ctrlOpts control.Options, -) ([]data.Collection, error) { +) ([]data.Collection, map[string]struct{}, error) { b, err := selector.ToSharePointBackup() if err != nil { - return nil, errors.Wrap(err, "sharePointDataCollection: parsing selector") + return nil, nil, errors.Wrap(err, "sharePointDataCollection: parsing selector") } var ( @@ -63,11 +63,11 @@ func DataCollections( su, ctrlOpts) if err != nil { - return nil, support.WrapAndAppend(site, err, errs) + return nil, nil, support.WrapAndAppend(site, err, errs) } case path.LibrariesCategory: - spcs, err = collectLibraries( + spcs, _, err = collectLibraries( ctx, itemClient, serv, @@ -77,7 +77,7 @@ func DataCollections( su, ctrlOpts) if err != nil { - return nil, support.WrapAndAppend(site, err, errs) + return nil, nil, support.WrapAndAppend(site, err, errs) } } @@ -85,7 +85,7 @@ func DataCollections( foldersComplete <- struct{}{} } - return collections, errs + return collections, nil, errs } func collectLists( @@ -134,7 +134,7 @@ func collectLibraries( scope selectors.SharePointScope, updater statusUpdater, ctrlOpts control.Options, -) ([]data.Collection, error) { +) ([]data.Collection, map[string]struct{}, error) { var ( collections = []data.Collection{} errs error @@ -152,12 +152,12 @@ func collectLibraries( updater.UpdateStatus, ctrlOpts) - odcs, err := colls.Get(ctx) + odcs, excludes, err := colls.Get(ctx) if err != nil { - return nil, support.WrapAndAppend(siteID, err, errs) + return nil, nil, support.WrapAndAppend(siteID, err, errs) } - return append(collections, odcs...), errs + return append(collections, odcs...), excludes, errs } type folderMatcher struct { diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 7d6c14748..247f9859b 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -261,7 +261,11 @@ func produceBackupDataCollections( closer() }() - return gc.DataCollections(ctx, sel, metadata, ctrlOpts) + // TODO(ashmrtn): When we're ready to wire up the global exclude list return + // all values. + cols, _, errs := gc.DataCollections(ctx, sel, metadata, ctrlOpts) + + return cols, errs } // --------------------------------------------------------------------------- From 48e6e8f08fdf46e43157965f1b815042c5ac10ac Mon Sep 17 00:00:00 2001 From: Keepers Date: Mon, 30 Jan 2023 18:23:08 -0700 Subject: [PATCH 06/46] enable onedrive backup for single user (#2317) ## Description unskips the backup load tests for a single user. Also adds a wrapper to make toggling restore load tests controllable for each test suite, instead of a file-wide configuration. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :robot: Test ## Issue(s) * #2279 ## Test Plan - [x] :green_heart: E2E --- src/pkg/repository/repository_load_test.go | 35 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/src/pkg/repository/repository_load_test.go b/src/pkg/repository/repository_load_test.go index 5725e5fda..744b57449 100644 --- a/src/pkg/repository/repository_load_test.go +++ b/src/pkg/repository/repository_load_test.go @@ -114,6 +114,7 @@ func runLoadTest( prefix, service string, usersUnderTest []string, bupSel, restSel selectors.Selector, + runRestore bool, ) { //revive:enable:context-as-argument t.Run(prefix+"_load_test_main", func(t *testing.T) { @@ -126,12 +127,33 @@ func runLoadTest( runBackupListLoadTest(t, ctx, r, service, bid) runBackupDetailsLoadTest(t, ctx, r, service, bid, usersUnderTest) + runRestoreLoadTest(t, ctx, r, prefix, service, bid, usersUnderTest, restSel, b, runRestore) + }) +} + +//revive:disable:context-as-argument +func runRestoreLoadTest( + t *testing.T, + ctx context.Context, + r repository.Repository, + prefix, service, backupID string, + usersUnderTest []string, + restSel selectors.Selector, + bup operations.BackupOperation, + runRestore bool, +) { + //revive:enable:context-as-argument + t.Run(prefix+"_load_test_restore", func(t *testing.T) { + if !runRestore { + t.Skip("restore load test is toggled off") + } + dest := tester.DefaultTestRestoreDestination() - rst, err := r.NewRestore(ctx, bid, restSel, dest) + rst, err := r.NewRestore(ctx, backupID, restSel, dest) require.NoError(t, err) - runRestoreLoadTest(t, ctx, rst, service, b.Results.ItemsWritten, usersUnderTest) + doRestoreLoadTest(t, ctx, rst, service, bup.Results.ItemsWritten, usersUnderTest) }) } @@ -240,7 +262,7 @@ func runBackupDetailsLoadTest( } //revive:disable:context-as-argument -func runRestoreLoadTest( +func doRestoreLoadTest( t *testing.T, ctx context.Context, r operations.RestoreOperation, @@ -408,6 +430,7 @@ func (suite *RepositoryLoadTestExchangeSuite) TestExchange() { "all_users", "exchange", suite.usersUnderTest, sel, sel, // same selection for backup and restore + true, ) } @@ -456,6 +479,7 @@ func (suite *RepositoryIndividualLoadTestExchangeSuite) TestExchange() { "single_user", "exchange", suite.usersUnderTest, sel, sel, // same selection for backup and restore + true, ) } @@ -504,6 +528,7 @@ func (suite *RepositoryLoadTestOneDriveSuite) TestOneDrive() { "all_users", "one_drive", suite.usersUnderTest, sel, sel, // same selection for backup and restore + false, ) } @@ -523,7 +548,6 @@ func TestRepositoryIndividualLoadTestOneDriveSuite(t *testing.T) { func (suite *RepositoryIndividualLoadTestOneDriveSuite) SetupSuite() { t := suite.T() - t.Skip("not running onedrive load tests atm") t.Parallel() suite.ctx, suite.repo, suite.acct, suite.st = initM365Repo(t) suite.usersUnderTest = singleUserSet(t) @@ -548,6 +572,7 @@ func (suite *RepositoryIndividualLoadTestOneDriveSuite) TestOneDrive() { "single_user", "one_drive", suite.usersUnderTest, sel, sel, // same selection for backup and restore + false, ) } @@ -596,6 +621,7 @@ func (suite *RepositoryLoadTestSharePointSuite) TestSharePoint() { "all_sites", "share_point", suite.sitesUnderTest, sel, sel, // same selection for backup and restore + false, ) } @@ -640,5 +666,6 @@ func (suite *RepositoryIndividualLoadTestSharePointSuite) TestSharePoint() { "single_site", "share_point", suite.sitesUnderTest, sel, sel, // same selection for backup and restore + false, ) } From da0fac20bf903afbaa64798583738d8ed16d713e Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Tue, 31 Jan 2023 07:12:04 +0530 Subject: [PATCH 07/46] Bump slash-command-dispatch and avoid running unverified commit in ok-to-test (#1756) ## Description Bumps ok-to-test to v3. Plus fix the issue where someone might have been able to run unverified code if they push between commenting `/ok-to-test` and the job starting. ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [x] :computer: CI/Deployment - [ ] :hamster: Trivial/Minor ## Issue(s) * https://github.com/alcionai/corso/pull/1651 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- .github/workflows/ci.yml | 16 ++++++++++++++-- .github/workflows/ok-to-test.yml | 3 +-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index acc1b68eb..170a63357 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -239,17 +239,29 @@ jobs: run: working-directory: src steps: - - name: Fail check + - name: Fail check if not repository_dispatch if: github.event_name != 'repository_dispatch' run: | echo "Workflow requires approval from a maintainer to run. It will be automatically rerun on approval." exit 1 + - uses: marocchino/sticky-pull-request-comment@v2 + if: github.event.client_payload.slash_command.args.named.sha != '' && contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.args.named.sha) + with: + message: | + Workflow run sha specified via `ok-to-test` is not the latest commit on PR. Run canceled. + + - name: Fail check if not head of PR + if: github.event.client_payload.slash_command.args.named.sha != '' && contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.args.named.sha) + run: | + echo "Workflow run sha specified is not the latest commit on PR. Exiting." + exit 1 + # add comment to PR with link to workflow run - uses: marocchino/sticky-pull-request-comment@v2 with: message: | - https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID + Test suite run will be available at https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID # Check out merge commit - name: Fork based /ok-to-test checkout diff --git a/.github/workflows/ok-to-test.yml b/.github/workflows/ok-to-test.yml index bd6a7db67..f48e49129 100644 --- a/.github/workflows/ok-to-test.yml +++ b/.github/workflows/ok-to-test.yml @@ -19,7 +19,7 @@ jobs: private_key: ${{ secrets.PRIVATE_KEY }} - name: Slash Command Dispatch - uses: peter-evans/slash-command-dispatch@v1 + uses: peter-evans/slash-command-dispatch@v3 env: TOKEN: ${{ steps.generate_token.outputs.token }} with: @@ -27,5 +27,4 @@ jobs: reaction-token: ${{ secrets.GITHUB_TOKEN }} issue-type: pull-request commands: ok-to-test - named-args: true permission: write From 1fd640a7549489c64d2baa4a9b5cc0bb1a1ce0ee Mon Sep 17 00:00:00 2001 From: Niraj Tolia Date: Mon, 30 Jan 2023 17:53:02 -0800 Subject: [PATCH 08/46] Add docs page for fault-tolerance (#2313) ## Description Add docs on Coso's fault tolerance and reliability ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included ## Type of change - [x] :world_map: Documentation --- CHANGELOG.md | 4 +++ website/docs/setup/fault-tolerance.md | 48 +++++++++++++++++++++++++++ website/sidebars.js | 4 +-- website/styles/Vocab/Base/accept.txt | 3 +- 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 website/docs/setup/fault-tolerance.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 16b893cc6..f4dd1f52c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] (alpha) +### Added + +- Document Corso's fault-tolerance and restartability features + ## [v0.2.0] (alpha) - 2023-1-29 ### Fixed diff --git a/website/docs/setup/fault-tolerance.md b/website/docs/setup/fault-tolerance.md new file mode 100644 index 000000000..1771f600a --- /dev/null +++ b/website/docs/setup/fault-tolerance.md @@ -0,0 +1,48 @@ +# Fault tolerance + +Given the millions of objects found in a typical Microsoft 365 tenant, +Corso is optimized for high-performance processing, hardened to +tolerate transient failures and, most importantly, able to restart backups. + +Corso’s fault-tolerance architecture is motivated by Microsoft’s Graph +API variable performance and throttling. Corso follows Microsoft’s +recommend best practices (for example, [correctly decorating API +traffic](https://learn.microsoft.com/en-us/sharepoint/dev/general-development/how-to-avoid-getting-throttled-or-blocked-in-sharepoint-online#how-to-decorate-your-http-traffic)) +and, in addition, implements a number of optimizations to improve +backup and restore reliability. + +## Recovery from transient failures + +Corso, at the HTTP layer, will retry requests (after a HTTP timeout, +for example) and will respect Graph API’s directives such as the +`retry-after` header to backoff when needed. This allows backups to +succeed in the face of transient or temporary failures. + +## Restarting from permanent API failures + +The Graph API can, for internal reasons, exhibit extended periods of +failures for particular Graph objects. In this scenario, bounded retries +will be ineffective. Unless invoked with the +fail fast option, Corso will skip over these failing objects. For +backups, it will move forward with backing up other objects belonging +to the user and, for restores, it will continue with trying to restore +any remaining objects. If a multi-user backed is in progress (via `*` +or by specifying multiple users with the `—user` argument), Corso will +also continue processing backups for the remaining users. In both +cases, Corso will exit with a non-zero exit code to reflect incomplete +backups or restores. + +On subsequent backup attempts, Corso will try to +minimize the work involved. If the previous backup was successful and +Corso’s stored state tokens haven’t expired, it will use [delta +queries](https://learn.microsoft.com/en-us/graph/delta-query-overview), +wherever supported, to perform incremental backups. + +If the previous backup for a user had resulted in a failure, Corso +uses a variety of fallback mechanisms to reduce the amount of data +downloaded and reduce the number of objects enumerated. For example, with +OneDrive, Corso won't redo downloads of data from Microsoft 365 or +uploads of data to the Corso repository if it had successfully backed +up that OneDrive file as a part of a previously incomplete and failed +backup. Even if the Graph API might not allow Corso to skip +downloading data, Corso can still skip another upload it to the repository. diff --git a/website/sidebars.js b/website/sidebars.js index fd92ea50e..2403412b7 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -19,8 +19,8 @@ const sidebars = { 'quickstart', { type: 'category', - label: 'Corso setup', - items: ['setup/concepts', 'setup/download', 'setup/m365-access', 'setup/configuration', 'setup/repos'], + label: 'Usage', + items: ['setup/concepts', 'setup/download', 'setup/m365-access', 'setup/configuration', 'setup/repos', 'setup/fault-tolerance'], }, { type: 'category', diff --git a/website/styles/Vocab/Base/accept.txt b/website/styles/Vocab/Base/accept.txt index eae43d149..69565b8a5 100644 --- a/website/styles/Vocab/Base/accept.txt +++ b/website/styles/Vocab/Base/accept.txt @@ -36,4 +36,5 @@ Atlassian SLAs runbooks stdout -stderr \ No newline at end of file +stderr +backoff \ No newline at end of file From bf404c0602f7c9cc636dbc83f8e16e687ae080ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 05:11:15 +0000 Subject: [PATCH 09/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20@loadable/com?= =?UTF-8?q?ponent=20from=205.15.2=20to=205.15.3=20in=20/website=20(#2325)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/package-lock.json | 16 ++++++++-------- website/package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index 15757ccf8..d5d5f5883 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -11,7 +11,7 @@ "@docusaurus/core": "2.3.0", "@docusaurus/plugin-google-gtag": "^2.3.0", "@docusaurus/preset-classic": "2.3.0", - "@loadable/component": "^5.15.2", + "@loadable/component": "^5.15.3", "@mdx-js/react": "^1.6.22", "animate.css": "^4.1.1", "clsx": "^1.2.1", @@ -2818,9 +2818,9 @@ "license": "MIT" }, "node_modules/@loadable/component": { - "version": "5.15.2", - "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", - "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.3.tgz", + "integrity": "sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==", "dependencies": { "@babel/runtime": "^7.7.7", "hoist-non-react-statics": "^3.3.1", @@ -2834,7 +2834,7 @@ "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "react": ">=16.3.0" + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" } }, "node_modules/@mdx-js/mdx": { @@ -16396,9 +16396,9 @@ "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, "@loadable/component": { - "version": "5.15.2", - "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.2.tgz", - "integrity": "sha512-ryFAZOX5P2vFkUdzaAtTG88IGnr9qxSdvLRvJySXcUA4B4xVWurUNADu3AnKPksxOZajljqTrDEDcYjeL4lvLw==", + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.15.3.tgz", + "integrity": "sha512-VOgYgCABn6+/7aGIpg7m0Ruj34tGetaJzt4bQ345FwEovDQZ+dua+NWLmuJKv8rWZyxOUSfoJkmGnzyDXH2BAQ==", "requires": { "@babel/runtime": "^7.7.7", "hoist-non-react-statics": "^3.3.1", diff --git a/website/package.json b/website/package.json index 4f64d0b6f..be749e6ce 100644 --- a/website/package.json +++ b/website/package.json @@ -17,7 +17,7 @@ "@docusaurus/core": "2.3.0", "@docusaurus/plugin-google-gtag": "^2.3.0", "@docusaurus/preset-classic": "2.3.0", - "@loadable/component": "^5.15.2", + "@loadable/component": "^5.15.3", "@mdx-js/react": "^1.6.22", "animate.css": "^4.1.1", "clsx": "^1.2.1", From 0f39b23a00c4375f74ef9b6f5d7dfb820a570cbf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 05:16:37 +0000 Subject: [PATCH 10/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20@iconify/reac?= =?UTF-8?q?t=20from=204.0.1=20to=204.1.0=20in=20/website=20(#2326)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/package-lock.json | 14 +++++++------- website/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index d5d5f5883..11a0d15e1 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "2.3.0", - "@iconify/react": "^4.0.1", + "@iconify/react": "^4.1.0", "autoprefixer": "^10.4.13", "postcss": "^8.4.21", "tailwindcss": "^3.2.4" @@ -2728,9 +2728,9 @@ } }, "node_modules/@iconify/react": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.0.1.tgz", - "integrity": "sha512-/DBJqh5K7W4f+d4kpvyJa/OTpVa3GfgrE9bZFAKP0vIWDr0cvVU9MVvbbkek216w9nLQhpJY/FeJtc6izB1PHw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.1.0.tgz", + "integrity": "sha512-Mf72i3TNNKpKCKxmo7kzqyrUdCgaoljpqtWmtqpqwyxoV4ukhnDsSRNLhf2yBnqGr3cVZsdj/i0FMpXIY0Qk0g==", "dev": true, "dependencies": { "@iconify/types": "^2.0.0" @@ -16330,9 +16330,9 @@ } }, "@iconify/react": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.0.1.tgz", - "integrity": "sha512-/DBJqh5K7W4f+d4kpvyJa/OTpVa3GfgrE9bZFAKP0vIWDr0cvVU9MVvbbkek216w9nLQhpJY/FeJtc6izB1PHw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@iconify/react/-/react-4.1.0.tgz", + "integrity": "sha512-Mf72i3TNNKpKCKxmo7kzqyrUdCgaoljpqtWmtqpqwyxoV4ukhnDsSRNLhf2yBnqGr3cVZsdj/i0FMpXIY0Qk0g==", "dev": true, "requires": { "@iconify/types": "^2.0.0" diff --git a/website/package.json b/website/package.json index be749e6ce..5d5670674 100644 --- a/website/package.json +++ b/website/package.json @@ -36,7 +36,7 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "2.3.0", - "@iconify/react": "^4.0.1", + "@iconify/react": "^4.1.0", "autoprefixer": "^10.4.13", "postcss": "^8.4.21", "tailwindcss": "^3.2.4" From 14be75e7aeed3c918db64efd911727d7326d8f8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 Jan 2023 16:14:29 +0000 Subject: [PATCH 11/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20github.com/aw?= =?UTF-8?q?s/aws-sdk-go=20from=201.44.189=20to=201.44.190=20in=20/src=20(#?= =?UTF-8?q?2327)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.189 to 1.44.190.
Release notes

Sourced from github.com/aws/aws-sdk-go's releases.

Release v1.44.190 (2023-01-30)

Service Client Updates

  • service/clouddirectory: Adds new service
    • Enabled FIPS endpoints for GovCloud (US) regions in SDK.
  • service/cloudformation: Updates service API and documentation
    • This feature provides a method of obtaining which regions a stackset has stack instances deployed in.
  • service/discovery: Updates service API
    • Update ImportName validation to 255 from the current length of 100
  • service/dlm: Adds new service
  • service/ec2: Updates service API and documentation
    • We add Prefix Lists as a new route destination option for LocalGatewayRoutes. This will allow customers to create routes to Prefix Lists. Prefix List routes will allow customers to group individual CIDR routes with the same target into a single route.
  • service/imagebuilder: Adds new service
  • service/kafka: Adds new service
  • service/mediaconvert: Adds new service
    • Enabled FIPS endpoints for GovCloud (US) regions in SDK.
  • service/swf: Adds new service
    • Enabled FIPS endpoints for GovCloud (US) regions in SDK.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go&package-manager=go_modules&previous-version=1.44.189&new-version=1.44.190)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- src/go.mod | 2 +- src/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/go.mod b/src/go.mod index b9cb5ec53..f424073cf 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 github.com/alcionai/clues v0.0.0-20230120231953-1cf61dbafc40 - github.com/aws/aws-sdk-go v1.44.189 + github.com/aws/aws-sdk-go v1.44.190 github.com/aws/aws-xray-sdk-go v1.8.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/src/go.sum b/src/go.sum index b8f926e79..365d78e35 100644 --- a/src/go.sum +++ b/src/go.sum @@ -62,8 +62,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/aws/aws-sdk-go v1.44.189 h1:9PBrjndH1uL5AN8818qI3duhQ4hgkMuLvqkJlg9MRyk= -github.com/aws/aws-sdk-go v1.44.189/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.190 h1:QC+Pf/Ooj7Waf2obOPZbIQOqr00hy4h54j3ZK9mvHcc= +github.com/aws/aws-sdk-go v1.44.190/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-xray-sdk-go v1.8.0 h1:0xncHZ588wB/geLjbM/esoW3FOEThWy2TJyb4VXfLFY= github.com/aws/aws-xray-sdk-go v1.8.0/go.mod h1:7LKe47H+j3evfvS1+q0wzpoaGXGrF3mUsfM+thqVO+A= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= From 3a5211590adbe27b2fa1211bf8c80b36c7db7689 Mon Sep 17 00:00:00 2001 From: Danny Date: Tue, 31 Jan 2023 11:38:28 -0500 Subject: [PATCH 12/46] GC: Beta Client Package (#2205) ## Description To add needed features, Kiota-Generated Beta Connector created ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included ## Type of change - [x] :sunflower: Feature ## Issue(s) * closes #2086 * closes #2174 * closes #2223 ## Test Plan Compilation and current CI tests will fail if the package does not align with current operational standards. - [x] :zap: Unit test - [x] :green_heart: E2E --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 4 + src/.golangci.yml | 12 +- src/go.mod | 14 +- src/go.sum | 28 +- .../connector/graph/betasdk/beta_client.go | 86 ++++ .../graph/betasdk/beta_client_test.go | 80 ++++ .../connector/graph/betasdk/kiota-lock.json | 131 ++++++ .../graph/betasdk/models/base_item.go | 379 +++++++++++++++++ .../graph/betasdk/models/canvas_layout.go | 103 +++++ .../graph/betasdk/models/canvas_layoutable.go | 16 + .../betasdk/models/horizontal_section.go | 133 ++++++ .../horizontal_section_collection_response.go | 75 ++++ ...izontal_section_collection_responseable.go | 14 + .../models/horizontal_section_column.go | 103 +++++ ...ntal_section_column_collection_response.go | 75 ++++ ..._section_column_collection_responseable.go | 14 + .../models/horizontal_section_columnable.go | 16 + .../models/horizontal_section_layout_type.go | 52 +++ .../betasdk/models/horizontal_sectionable.go | 18 + .../models/meta_data_key_string_pair.go | 123 ++++++ ...ata_key_string_pair_collection_response.go | 75 ++++ ...key_string_pair_collection_responseable.go | 14 + .../models/meta_data_key_string_pairable.go | 17 + .../models/meta_data_key_value_pair.go | 135 ++++++ ...data_key_value_pair_collection_response.go | 75 ++++ ..._key_value_pair_collection_responseable.go | 14 + .../models/meta_data_key_value_pairable.go | 18 + .../graph/betasdk/models/page_layout_type.go | 40 ++ .../betasdk/models/page_promotion_type.go | 40 ++ .../graph/betasdk/models/publication_facet.go | 123 ++++++ .../betasdk/models/publication_facetable.go | 17 + .../graph/betasdk/models/reactions_facet.go | 149 +++++++ .../betasdk/models/reactions_facetable.go | 19 + .../betasdk/models/section_emphasis_type.go | 43 ++ .../models/server_processed_content.go | 294 +++++++++++++ .../models/server_processed_contentable.go | 25 ++ .../graph/betasdk/models/site_access_type.go | 37 ++ .../graph/betasdk/models/site_page.go | 387 ++++++++++++++++++ .../models/site_page_collection_response.go | 76 ++++ .../site_page_collection_responseable.go | 14 + .../graph/betasdk/models/site_pageable.go | 36 ++ .../betasdk/models/site_security_level.go | 52 +++ .../graph/betasdk/models/site_settings.go | 123 ++++++ .../graph/betasdk/models/site_settingsable.go | 17 + .../graph/betasdk/models/standard_web_part.go | 88 ++++ .../standard_web_part_collection_response.go | 75 ++++ ...andard_web_part_collection_responseable.go | 14 + .../betasdk/models/standard_web_partable.go | 15 + .../graph/betasdk/models/text_web_part.go | 62 +++ .../text_web_part_collection_response.go | 75 ++++ .../text_web_part_collection_responseable.go | 14 + .../graph/betasdk/models/text_web_partable.go | 13 + .../graph/betasdk/models/title_area.go | 360 ++++++++++++++++ .../betasdk/models/title_area_layout_type.go | 43 ++ .../models/title_area_text_alignment_type.go | 37 ++ .../graph/betasdk/models/title_areaable.go | 33 ++ .../graph/betasdk/models/vertical_section.go | 104 +++++ .../betasdk/models/vertical_sectionable.go | 16 + .../graph/betasdk/models/web_part.go | 59 +++ .../models/web_part_collection_response.go | 75 ++++ .../web_part_collection_responseable.go | 14 + .../graph/betasdk/models/web_part_data.go | 251 ++++++++++++ .../graph/betasdk/models/web_part_dataable.go | 26 ++ .../graph/betasdk/models/web_part_position.go | 175 ++++++++ .../betasdk/models/web_part_positionable.go | 21 + .../graph/betasdk/models/web_partable.go | 12 + .../betasdk/sites/count_request_builder.go | 116 ++++++ .../sites/item_pages_count_request_builder.go | 115 ++++++ ...rizontal_sections_count_request_builder.go | 122 ++++++ ...horizontal_section_item_request_builder.go | 260 ++++++++++++ ...ions_item_columns_count_request_builder.go | 117 ++++++ ...tal_section_column_item_request_builder.go | 247 +++++++++++ ...mns_item_webparts_count_request_builder.go | 111 +++++ ...et_position_of_web_part_request_builder.go | 96 +++++ ...m_columns_item_webparts_request_builder.go | 180 ++++++++ ..._webparts_web_part_item_request_builder.go | 209 ++++++++++ ...l_sections_item_columns_request_builder.go | 180 ++++++++ ...out_horizontal_sections_request_builder.go | 179 ++++++++ ...ages_item_canvas_layout_request_builder.go | 230 +++++++++++ ...layout_vertical_section_request_builder.go | 226 ++++++++++ ..._section_webparts_count_request_builder.go | 107 +++++ ...et_position_of_web_part_request_builder.go | 97 +++++ ...rtical_section_webparts_request_builder.go | 177 ++++++++ ..._webparts_web_part_item_request_builder.go | 209 ++++++++++ ...web_parts_by_position_post_request_body.go | 182 ++++++++ ...parts_by_position_post_request_bodyable.go | 17 + ...t_web_parts_by_position_request_builder.go | 91 ++++ ...item_get_web_parts_by_position_response.go | 89 ++++ ..._get_web_parts_by_position_responseable.go | 16 + ...item_pages_item_publish_request_builder.go | 82 ++++ ...es_item_web_parts_count_request_builder.go | 103 +++++ ...et_position_of_web_part_request_builder.go | 96 +++++ ...em_pages_item_web_parts_request_builder.go | 172 ++++++++ ...web_parts_web_part_item_request_builder.go | 209 ++++++++++ .../sites/item_pages_request_builder.go | 176 ++++++++ ...em_pages_site_page_item_request_builder.go | 237 +++++++++++ .../sites/item_sites_count_request_builder.go | 103 +++++ .../item_sites_site_item_request_builder.go | 104 +++++ .../sites/site_item_request_builder.go | 249 +++++++++++ .../connector/graph_connector_helper_test.go | 26 +- src/internal/connector/sharepoint/pageInfo.go | 3 +- .../connector/sharepoint/pageInfo_test.go | 11 +- .../connector/sharepoint/site_page.go | 187 --------- .../connector/sharepoint/site_pageable.go | 24 -- 105 files changed, 9803 insertions(+), 252 deletions(-) create mode 100644 src/internal/connector/graph/betasdk/beta_client.go create mode 100644 src/internal/connector/graph/betasdk/beta_client_test.go create mode 100644 src/internal/connector/graph/betasdk/kiota-lock.json create mode 100644 src/internal/connector/graph/betasdk/models/base_item.go create mode 100644 src/internal/connector/graph/betasdk/models/canvas_layout.go create mode 100644 src/internal/connector/graph/betasdk/models/canvas_layoutable.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_column.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_columnable.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go create mode 100644 src/internal/connector/graph/betasdk/models/horizontal_sectionable.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_value_pair.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/meta_data_key_value_pairable.go create mode 100644 src/internal/connector/graph/betasdk/models/page_layout_type.go create mode 100644 src/internal/connector/graph/betasdk/models/page_promotion_type.go create mode 100644 src/internal/connector/graph/betasdk/models/publication_facet.go create mode 100644 src/internal/connector/graph/betasdk/models/publication_facetable.go create mode 100644 src/internal/connector/graph/betasdk/models/reactions_facet.go create mode 100644 src/internal/connector/graph/betasdk/models/reactions_facetable.go create mode 100644 src/internal/connector/graph/betasdk/models/section_emphasis_type.go create mode 100644 src/internal/connector/graph/betasdk/models/server_processed_content.go create mode 100644 src/internal/connector/graph/betasdk/models/server_processed_contentable.go create mode 100644 src/internal/connector/graph/betasdk/models/site_access_type.go create mode 100644 src/internal/connector/graph/betasdk/models/site_page.go create mode 100644 src/internal/connector/graph/betasdk/models/site_page_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/site_page_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/site_pageable.go create mode 100644 src/internal/connector/graph/betasdk/models/site_security_level.go create mode 100644 src/internal/connector/graph/betasdk/models/site_settings.go create mode 100644 src/internal/connector/graph/betasdk/models/site_settingsable.go create mode 100644 src/internal/connector/graph/betasdk/models/standard_web_part.go create mode 100644 src/internal/connector/graph/betasdk/models/standard_web_part_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/standard_web_part_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/standard_web_partable.go create mode 100644 src/internal/connector/graph/betasdk/models/text_web_part.go create mode 100644 src/internal/connector/graph/betasdk/models/text_web_part_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/text_web_part_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/text_web_partable.go create mode 100644 src/internal/connector/graph/betasdk/models/title_area.go create mode 100644 src/internal/connector/graph/betasdk/models/title_area_layout_type.go create mode 100644 src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go create mode 100644 src/internal/connector/graph/betasdk/models/title_areaable.go create mode 100644 src/internal/connector/graph/betasdk/models/vertical_section.go create mode 100644 src/internal/connector/graph/betasdk/models/vertical_sectionable.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_collection_response.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_collection_responseable.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_data.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_dataable.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_position.go create mode 100644 src/internal/connector/graph/betasdk/models/web_part_positionable.go create mode 100644 src/internal/connector/graph/betasdk/models/web_partable.go create mode 100644 src/internal/connector/graph/betasdk/sites/count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_horizontal_section_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_horizontal_section_column_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_item_get_position_of_web_part_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_web_part_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_item_get_position_of_web_part_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_web_part_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_body.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_bodyable.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_response.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_responseable.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_publish_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_pages_site_page_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_sites_count_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/item_sites_site_item_request_builder.go create mode 100644 src/internal/connector/graph/betasdk/sites/site_item_request_builder.go delete mode 100644 src/internal/connector/sharepoint/site_page.go delete mode 100644 src/internal/connector/sharepoint/site_pageable.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 170a63357..7083e885b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,7 @@ jobs: working-directory: src steps: - uses: actions/checkout@v3 - + # single setup and sum cache handling here. # the results will cascade onto both testing and linting. - name: Setup Golang with cache diff --git a/CHANGELOG.md b/CHANGELOG.md index f4dd1f52c..1b18a4d04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Check if the user specified for an exchange backup operation has a mailbox. + +### Changed + +- BetaClient introduced. Enables Corso to be able to interact with SharePoint Page objects. Package located `/internal/connector/graph/betasdk` - Handle case where user's drive has not been initialized - Inline attachments (e.g. copy/paste ) are discovered and backed up correctly ([#2163](https://github.com/alcionai/corso/issues/2163)) - Guest and External users (for cloud accounts) and non-on-premise users (for systems that use on-prem AD syncs) are now excluded from backup and restore operations. diff --git a/src/.golangci.yml b/src/.golangci.yml index db883efc4..bda9330be 100644 --- a/src/.golangci.yml +++ b/src/.golangci.yml @@ -3,7 +3,6 @@ run: linters: enable: - - gci - gofmt - gofumpt - errcheck @@ -106,3 +105,14 @@ issues: linters: - forbidigo text: "context.(Background|TODO)" + - path: internal/connector/graph/betasdk + linters: + - wsl + - revive + - gci + - lll + - gofmt + - gofumpt + - misspell + - errcheck + diff --git a/src/go.mod b/src/go.mod index f424073cf..1b05ec06b 100644 --- a/src/go.mod +++ b/src/go.mod @@ -10,12 +10,12 @@ require ( github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 github.com/kopia/kopia v0.12.2-0.20230123092305-e5387cec0acb - github.com/microsoft/kiota-abstractions-go v0.15.2 - github.com/microsoft/kiota-authentication-azure-go v0.5.0 - github.com/microsoft/kiota-http-go v0.11.0 + github.com/microsoft/kiota-abstractions-go v0.16.0 + github.com/microsoft/kiota-authentication-azure-go v0.6.0 + github.com/microsoft/kiota-http-go v0.13.0 github.com/microsoft/kiota-serialization-json-go v0.7.2 - github.com/microsoftgraph/msgraph-sdk-go v0.50.0 - github.com/microsoftgraph/msgraph-sdk-go-core v0.31.1 + github.com/microsoftgraph/msgraph-sdk-go v0.53.0 + github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0 github.com/pkg/errors v0.9.1 github.com/rudderlabs/analytics-go v3.3.3+incompatible github.com/spatialcurrent/go-lazy v0.0.0-20211115014721-47315cc003d1 @@ -53,8 +53,8 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect diff --git a/src/go.sum b/src/go.sum index 365d78e35..72af64b2d 100644 --- a/src/go.sum +++ b/src/go.sum @@ -36,12 +36,12 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 h1:sVW/AFBTGyJxDaMYlq0ct3jUXTtj12tQ6zE2GZUgVQw= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0 h1:VuHAcMq8pU1IWNT/m5yRaGqbK0BiQKHT8X4DTp9CHdI= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.3.0/go.mod h1:tZoQYdDZNOiIjdSn0dVWVfl0NEPGOJqVLzSrcFk4Is0= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 h1:t/W5MYAuQy81cvM8VUNfRLzhtKpXhVUAN7Cd7KVbTyc= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0/go.mod h1:NBanQUfSWiWn3QEpWDTCU0IjBECKOYvl2R8xdRtMtiM= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 h1:+5VZ72z0Qan5Bog5C+ZkgSqUbeVUd9wgtHOrIKuc5b8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -269,22 +269,22 @@ github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09 github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/microsoft/kiota-abstractions-go v0.15.2 h1:Pp78BbqPvkF2mAMH0Ph37ymwfSH7uF9iYfY1fZ8g630= -github.com/microsoft/kiota-abstractions-go v0.15.2/go.mod h1:RT/s9sCzg49i4iO7e2qhyWmX+DlJDgC0P+Wp8fKQQfo= -github.com/microsoft/kiota-authentication-azure-go v0.5.0 h1:RVA/tTgMnDIN3u4qPZtvYvVRsQDOFkd3yvi6KXjZJko= -github.com/microsoft/kiota-authentication-azure-go v0.5.0/go.mod h1:1Io6h+88FlDRmrajdjSnXPz8oyObUVjNuQZLhrF9kQk= -github.com/microsoft/kiota-http-go v0.11.0 h1:0K0y/wZcTvEEX2Xdj5tngJqknqYQpArLdtjB/fo88Dc= -github.com/microsoft/kiota-http-go v0.11.0/go.mod h1:4D6vMjT7jQ3IRAJrpFoaDtfS9eMaSjQKHY6ETSV2cc0= +github.com/microsoft/kiota-abstractions-go v0.16.0 h1:DZ1L4YsRsQw39iPGnVq2fQkqLXMsazdPwmWsnaH4EZg= +github.com/microsoft/kiota-abstractions-go v0.16.0/go.mod h1:RT/s9sCzg49i4iO7e2qhyWmX+DlJDgC0P+Wp8fKQQfo= +github.com/microsoft/kiota-authentication-azure-go v0.6.0 h1:Il9bLO34J6D8DY89xYAXoGh9muvlphayqG4eihyT6B8= +github.com/microsoft/kiota-authentication-azure-go v0.6.0/go.mod h1:EJCHiLWLXW1/mSgX7lYReAhVO37MzRT5Xi2mcPTwCRQ= +github.com/microsoft/kiota-http-go v0.13.0 h1:CZSC+UrBSwjIvLlVh+AEPsDbD0c17AWd/QPRHIljd8k= +github.com/microsoft/kiota-http-go v0.13.0/go.mod h1:aWtBlFhCetH0JmouvN3hiSaJoqCEdVHPqlrcLYExs3k= github.com/microsoft/kiota-serialization-form-go v0.2.0 h1:jgPE+8DtrWhL+KwnAwRm13HnMNydbelC/NP9wRGwDUo= github.com/microsoft/kiota-serialization-form-go v0.2.0/go.mod h1:chOuh09tO7IrNtubAumdlG5wnrcYdMkjV7joVVGDyGs= github.com/microsoft/kiota-serialization-json-go v0.7.2 h1:DSb4fNDi5O+DqJwrHo+vRy2kSvfxG5VtN6m1EHzn5Vw= github.com/microsoft/kiota-serialization-json-go v0.7.2/go.mod h1:Ojum5prlijopyCOZ2XctRcVlE2pU8h+43r3tMdiWoDU= github.com/microsoft/kiota-serialization-text-go v0.6.0 h1:3N2vftYZlwKdog69AN7ha+FZT0QxPG7xp/hLv0/W2OQ= github.com/microsoft/kiota-serialization-text-go v0.6.0/go.mod h1:OUA4dNH+f6afiJUs+rQAatJos7QVF5PJkyrqoD89lx4= -github.com/microsoftgraph/msgraph-sdk-go v0.50.0 h1:yfPBDr7+tSdq8jKiNCvY5XzQji1kZzOHXvxQ9XxQ4E4= -github.com/microsoftgraph/msgraph-sdk-go v0.50.0/go.mod h1:XoTT9lzRSersVV4/lsFup3sOLfOLEf2dMsLckiAAKq8= -github.com/microsoftgraph/msgraph-sdk-go-core v0.31.1 h1:aVvnO5l8qLCEcvELc5n9grt7UXhAVtpog1QeQKLMlTE= -github.com/microsoftgraph/msgraph-sdk-go-core v0.31.1/go.mod h1:RE4F2qGCTehGtQGc9Txafc4l+XMpbjYuO4amDLFgOWE= +github.com/microsoftgraph/msgraph-sdk-go v0.53.0 h1:HpQd1Nvr8yQNeqhDuiVSbqn1fkHsFbRFDmnuhhXJXOQ= +github.com/microsoftgraph/msgraph-sdk-go v0.53.0/go.mod h1:BZLyon4n4T4EuLIAlX+kJ5JgneFTXVQDah1AJuq3FRY= +github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0 h1:cDL3ov/IZ2ZarUJdGGPsdR+46ALdd3CRAiDBIylLCoA= +github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0/go.mod h1:d0mU3PQAWnN/C4CwPJEZz2QhesrnR5UDnqRu2ODWPkI= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.45 h1:g4IeM9M9pW/Lo8AGGNOjBZYlvmtlE1N5TQEYWXRWzIs= diff --git a/src/internal/connector/graph/betasdk/beta_client.go b/src/internal/connector/graph/betasdk/beta_client.go new file mode 100644 index 000000000..515e1de95 --- /dev/null +++ b/src/internal/connector/graph/betasdk/beta_client.go @@ -0,0 +1,86 @@ +package betasdk + +import ( + i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/sites" + absser "github.com/microsoft/kiota-abstractions-go" + kioser "github.com/microsoft/kiota-abstractions-go/serialization" + kform "github.com/microsoft/kiota-serialization-form-go" + kw "github.com/microsoft/kiota-serialization-json-go" + ktext "github.com/microsoft/kiota-serialization-text-go" + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" +) + +// BetaClient the main entry point of the SDK, exposes the configuration and the fluent API. +// Minimal Beta Connector: +// Details on how the Code was generated is present in `kioter-lock.json`. +// NOTE: kiota gen file is altered to indicate what files are included in the created +// +// Changes to Sites Directory: +// Access files send requests with an adapter's with ASync() support. +// This feature is not enabled in v1.0. Manually changed in remaining files. +// Additionally, only calls that begin as client.SitesBy(siteID).Pages() have an endpoint. +// +// The use case specific to Pages(). All other requests should be routed to the /internal/connector/graph.Servicer +// Specifics on `betaClient.SitesById(siteID).Pages` are located: sites/site_item_request_builder.go +// +// Required model files are identified as `modelFiles` in kiota-lock.json. Directory -> betasdk/models +// Required access files are identified as `sitesFiles` in kiota-lock.json. Directory -> betasdk/sites +// +// BetaClient minimal msgraph-beta-sdk-go for connecting to msgraph-beta-sdk-go +// for retrieving `SharePoint.Pages`. Code is generated from kiota.dev. +// requestAdapter is registered with the following the serializers: +// -- "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", +// -- "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", +// -- "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" +type BetaClient struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter *msgraphsdk.GraphRequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// NewBetaClient instantiates a new BetaClient and sets the default values. +// func NewBetaClient(requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter)(*BetaClient) { +func NewBetaClient(requestAdapter *msgraphsdk.GraphRequestAdapter) *BetaClient { + m := &BetaClient{} + m.pathParameters = make(map[string]string) + m.urlTemplate = "{+baseurl}" + m.requestAdapter = requestAdapter + absser.RegisterDefaultSerializer(func() kioser.SerializationWriterFactory { + return kw.NewJsonSerializationWriterFactory() + }) + absser.RegisterDefaultSerializer(func() kioser.SerializationWriterFactory { + return ktext.NewTextSerializationWriterFactory() + }) + absser.RegisterDefaultSerializer(func() kioser.SerializationWriterFactory { + return kform.NewFormSerializationWriterFactory() + }) + absser.RegisterDefaultDeserializer(func() kioser.ParseNodeFactory { + return kw.NewJsonParseNodeFactory() + }) + absser.RegisterDefaultDeserializer(func() kioser.ParseNodeFactory { + return ktext.NewTextParseNodeFactory() + }) + absser.RegisterDefaultDeserializer(func() kioser.ParseNodeFactory { + return kform.NewFormParseNodeFactory() + }) + + if m.requestAdapter.GetBaseUrl() == "" { + m.requestAdapter.SetBaseUrl("https://graph.microsoft.com/beta") + } + return m +} + +// SitesById provides operations to manage the collection of site entities. +func (m *BetaClient) SitesById(id string) *i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411.SiteItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["site%2Did"] = id + } + return i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411.NewSiteItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} diff --git a/src/internal/connector/graph/betasdk/beta_client_test.go b/src/internal/connector/graph/betasdk/beta_client_test.go new file mode 100644 index 000000000..84f2db6c5 --- /dev/null +++ b/src/internal/connector/graph/betasdk/beta_client_test.go @@ -0,0 +1,80 @@ +package betasdk + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/tester" + "github.com/alcionai/corso/src/pkg/account" +) + +type BetaClientSuite struct { + suite.Suite + credentials account.M365Config +} + +func TestBetaClientSuite(t *testing.T) { + suite.Run(t, new(BetaClientSuite)) +} + +func (suite *BetaClientSuite) SetupSuite() { + t := suite.T() + a := tester.NewM365Account(t) + m365, err := a.M365Config() + require.NoError(t, err) + + suite.credentials = m365 +} + +func (suite *BetaClientSuite) TestCreateBetaClient() { + t := suite.T() + adpt, err := graph.CreateAdapter( + suite.credentials.AzureTenantID, + suite.credentials.AzureClientID, + suite.credentials.AzureClientSecret, + ) + + require.NoError(t, err) + + client := NewBetaClient(adpt) + assert.NotNil(t, client) +} + +// TestBasicClientGetFunctionality. Tests that adapter is able +// to parse retrieved Site Page. Additional tests should +// be handled within the /internal/connector/sharepoint when +// additional features are added. +func (suite *BetaClientSuite) TestBasicClientGetFunctionality() { + ctx, flush := tester.NewContext() + defer flush() + t := suite.T() + adpt, err := graph.CreateAdapter( + suite.credentials.AzureTenantID, + suite.credentials.AzureClientID, + suite.credentials.AzureClientSecret, + ) + + require.NoError(t, err) + client := NewBetaClient(adpt) + require.NotNil(t, client) + + siteID := tester.M365SiteID(t) + // TODO(dadams39) document allowable calls in main + collection, err := client.SitesById(siteID).Pages().Get(ctx, nil) + // Ensures that the client is able to receive data from beta + // Not Registered Error: content type application/json does not have a factory registered to be parsed + require.NoError(t, err) + + for _, page := range collection.GetValue() { + assert.NotNil(t, page, "betasdk call for page does not return value.") + + if page != nil { + t.Logf("Page :%s ", *page.GetName()) + assert.NotNil(t, page.GetId()) + } + } +} diff --git a/src/internal/connector/graph/betasdk/kiota-lock.json b/src/internal/connector/graph/betasdk/kiota-lock.json new file mode 100644 index 000000000..21a111aef --- /dev/null +++ b/src/internal/connector/graph/betasdk/kiota-lock.json @@ -0,0 +1,131 @@ +{ + "lockFileVersion": "1.0.0", + "kiotaVersion": "0.10.0.0", + "clientClassName": "BetaClient", + "clientNamespaceName": "github.com/alcionai/corso/src/internal/connector/graph/betasdk", + "language": "Go", + "betaVersion": "0.53.0", + "usesBackingStore": false, + "includeAdditionalData": true, + "serializers": [ + "Microsoft.Kiota.Serialization.Json.JsonSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Text.TextSerializationWriterFactory", + "Microsoft.Kiota.Serialization.Form.FormSerializationWriterFactory" + ], + "deserializers": [ + "Microsoft.Kiota.Serialization.Json.JsonParseNodeFactory", + "Microsoft.Kiota.Serialization.Text.TextParseNodeFactory", + "Microsoft.Kiota.Serialization.Form.FormParseNodeFactory" + ], + "structuredMimeTypes": [ + "application/json", + "text/plain", + "application/x-www-form-urlencoded" + ], + "includePatterns": [ + "**/sites/**" + ], + "excludePatterns": [ + "**/admin/**", + "**/users/**", + "**/groups/**", + "**/onenote/**" + ], + "sitesFiles": [ + "count_request_builder.go", + "item_pages_count_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_count_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_horizontal_section_item_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_count_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_horizontal_section_column_item_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_count_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_item_get_position_of_web_part_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_web_part_item_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_item_columns_request_builder.go", + "item_pages_item_canvas_layout_horizontal_sections_request_builder.go", + "item_pages_item_canvas_layout_request_builder.go", + "item_pages_item_canvas_layout_vertical_section_request_builder.go", + "item_pages_item_canvas_layout_vertical_section_webparts_count_request_builder.go", + "item_pages_item_canvas_layout_vertical_section_webparts_item_get_position_of_web_part_request_builder.go", + "item_pages_item_canvas_layout_vertical_section_webparts_request_builder.go", + "item_pages_item_canvas_layout_vertical_section_webparts_web_part_item_request_builder.go", + "item_pages_item_get_web_parts_by_position_post_request_body.go", + "item_pages_item_get_web_parts_by_position_post_request_bodyable.go", + "item_pages_item_get_web_parts_by_position_request_builder.go", + "item_pages_item_get_web_parts_by_position_response.go", + "item_pages_item_get_web_parts_by_position_responseable.go", + "item_pages_item_publish_request_builder.go", + "item_pages_item_web_parts_count_request_builder.go", + "item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go", + "item_pages_item_web_parts_request_builder.go", + "item_pages_item_web_parts_web_part_item_request_builder.go", + "item_pages_request_builder.go", + "item_pages_site_page_item_request_builder.go", + "item_sites_count_request_builder.go", + "item_sites_site_item_request_builder.go", + "site_item_request_builder.go" + ], + "modelFiles":[ + "base_item.go", + "page_layout_type.go", + "standard_web_partable.go", + "canvas_layout.go", + "page_promotion_type.go", + "text_web_part.go", + "canvas_layoutable.go", + "publication_facet.go", + "text_web_part_collection_response.go", + "horizontal_section.go", + "publication_facetable.go", + "text_web_part_collection_responseable.go", + "horizontal_section_collection_response.go", + "reactions_facet.go", + "text_web_partable.go", + "horizontal_section_collection_responseable.go", + "reactions_facetable.go", + "title_area.go", + "horizontal_section_column.go", + "section_emphasis_type.go", + "title_area_layout_type.go", + "horizontal_section_column_collection_response.go", + "server_processed_content.go", + "title_area_text_alignment_type.go", + "horizontal_section_column_collection_responseable.go", + "server_processed_contentable.go", + "title_areaable.go", + "horizontal_section_columnable.go", + "site_access_type.go", + "vertical_section.go", + "horizontal_section_layout_type.go", + "site_page.go", + "vertical_sectionable.go", + "horizontal_sectionable.go", + "site_page_collection_response.go", + "web_part.go", + "meta_data_key_string_pair.go", + "site_page_collection_responseable.go", + "web_part_collection_response.go", + "meta_data_key_string_pair_collection_response.go", + "site_pageable.go", + "web_part_collection_responseable.go", + "meta_data_key_string_pair_collection_responseable.go", + "site_security_level.go", + "web_part_data.go", + "meta_data_key_string_pairable.go", + "site_settings.go", + "web_part_dataable.go", + "meta_data_key_value_pair.go", + "site_settingsable.go", + "web_part_position.go", + "meta_data_key_value_pair_collection_response.go", + "standard_web_part.go", + "web_part_positionable.go", + "meta_data_key_value_pair_collection_responseable.go", + "standard_web_part_collection_response.go", + "web_partable.go", + "meta_data_key_value_pairable.go", + "standard_web_part_collection_responseable.go" + ], + "disabledValidationRules": [] +} diff --git a/src/internal/connector/graph/betasdk/models/base_item.go b/src/internal/connector/graph/betasdk/models/base_item.go new file mode 100644 index 000000000..880262348 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/base_item.go @@ -0,0 +1,379 @@ +package models + +import ( + i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e "time" + + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// BaseItem +type BaseItem struct { + msmodel.Entity + // Identity of the user, device, or application which created the item. Read-only. + createdBy msmodel.IdentitySetable + // The createdByUser property + createdByUser msmodel.Userable + // Date and time of item creation. Read-only. + createdDateTime *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time + // The description property + description *string + // ETag for the item. Read-only. + eTag *string + // Identity of the user, device, and application which last modified the item. Read-only. + lastModifiedBy msmodel.IdentitySetable + // The lastModifiedByUser property + lastModifiedByUser msmodel.Userable + // Date and time the item was last modified. Read-only. + lastModifiedDateTime *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time + // The name of the item. Read-write. + name *string + // Parent information, if the item has a parent. Read-write. + parentReference msmodel.ItemReferenceable + // URL that displays the resource in the browser. Read-only. + webUrl *string +} + +// NewBaseItem instantiates a new baseItem and sets the default values. +func NewBaseItem() *BaseItem { + m := &BaseItem{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateBaseItemFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateBaseItemFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + if parseNode != nil { + mappingValueNode, err := parseNode.GetChildNode("@odata.type") + if err != nil { + return nil, err + } + if mappingValueNode != nil { + mappingValue, err := mappingValueNode.GetStringValue() + if err != nil { + return nil, err + } + if mappingValue != nil { + switch *mappingValue { + case "#microsoft.graph.drive": + return msmodel.NewDrive(), nil + case "#microsoft.graph.driveItem": + return msmodel.NewDriveItem(), nil + case "#microsoft.graph.list": + return msmodel.NewList(), nil + case "#microsoft.graph.listItem": + return msmodel.NewListItem(), nil + case "#microsoft.graph.sharedDriveItem": + return msmodel.NewSharedDriveItem(), nil + case "#microsoft.graph.site": + return msmodel.NewSite(), nil + case "#microsoft.graph.sitePage": + return NewSitePage(), nil + } + } + } + } + return NewBaseItem(), nil +} + +// GetCreatedBy gets the createdBy property value. Identity of the user, device, or application which created the item. Read-only. +func (m *BaseItem) GetCreatedBy() msmodel.IdentitySetable { + return m.createdBy +} + +// GetCreatedByUser gets the createdByUser property value. The createdByUser property +func (m *BaseItem) GetCreatedByUser() msmodel.Userable { + return m.createdByUser +} + +// GetCreatedDateTime gets the createdDateTime property value. Date and time of item creation. Read-only. +func (m *BaseItem) GetCreatedDateTime() *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time { + return m.createdDateTime +} + +// GetDescription gets the description property value. The description property +func (m *BaseItem) GetDescription() *string { + return m.description +} + +// GetETag gets the eTag property value. ETag for the item. Read-only. +func (m *BaseItem) GetETag() *string { + return m.eTag +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *BaseItem) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + res["createdBy"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateIdentitySetFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetCreatedBy(val.(msmodel.IdentitySetable)) + } + return nil + } + res["createdByUser"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateUserFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetCreatedByUser(val.(msmodel.Userable)) + } + return nil + } + res["createdDateTime"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetTimeValue() + if err != nil { + return err + } + if val != nil { + m.SetCreatedDateTime(val) + } + return nil + } + res["description"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetDescription(val) + } + return nil + } + res["eTag"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetETag(val) + } + return nil + } + res["lastModifiedBy"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateIdentitySetFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetLastModifiedBy(val.(msmodel.IdentitySetable)) + } + return nil + } + res["lastModifiedByUser"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateUserFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetLastModifiedByUser(val.(msmodel.Userable)) + } + return nil + } + res["lastModifiedDateTime"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetTimeValue() + if err != nil { + return err + } + if val != nil { + m.SetLastModifiedDateTime(val) + } + return nil + } + res["name"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetName(val) + } + return nil + } + res["parentReference"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateItemReferenceFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetParentReference(val.(msmodel.ItemReferenceable)) + } + return nil + } + res["webUrl"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetWebUrl(val) + } + return nil + } + return res +} + +// GetLastModifiedBy gets the lastModifiedBy property value. Identity of the user, device, and application which last modified the item. Read-only. +func (m *BaseItem) GetLastModifiedBy() msmodel.IdentitySetable { + return m.lastModifiedBy +} + +// GetLastModifiedByUser gets the lastModifiedByUser property value. The lastModifiedByUser property +func (m *BaseItem) GetLastModifiedByUser() msmodel.Userable { + return m.lastModifiedByUser +} + +// GetLastModifiedDateTime gets the lastModifiedDateTime property value. Date and time the item was last modified. Read-only. +func (m *BaseItem) GetLastModifiedDateTime() *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time { + return m.lastModifiedDateTime +} + +// GetName gets the name property value. The name of the item. Read-write. +func (m *BaseItem) GetName() *string { + return m.name +} + +// GetParentReference gets the parentReference property value. Parent information, if the item has a parent. Read-write. +func (m *BaseItem) GetParentReference() msmodel.ItemReferenceable { + return m.parentReference +} + +// GetWebUrl gets the webUrl property value. URL that displays the resource in the browser. Read-only. +func (m *BaseItem) GetWebUrl() *string { + return m.webUrl +} + +// Serialize serializes information the current object +func (m *BaseItem) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteObjectValue("createdBy", m.GetCreatedBy()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("createdByUser", m.GetCreatedByUser()) + if err != nil { + return err + } + } + { + err = writer.WriteTimeValue("createdDateTime", m.GetCreatedDateTime()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("description", m.GetDescription()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("eTag", m.GetETag()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("lastModifiedBy", m.GetLastModifiedBy()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("lastModifiedByUser", m.GetLastModifiedByUser()) + if err != nil { + return err + } + } + { + err = writer.WriteTimeValue("lastModifiedDateTime", m.GetLastModifiedDateTime()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("name", m.GetName()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("parentReference", m.GetParentReference()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("webUrl", m.GetWebUrl()) + if err != nil { + return err + } + } + return nil +} + +// SetCreatedBy sets the createdBy property value. Identity of the user, device, or application which created the item. Read-only. +func (m *BaseItem) SetCreatedBy(value msmodel.IdentitySetable) { + m.createdBy = value +} + +// SetCreatedByUser sets the createdByUser property value. The createdByUser property +func (m *BaseItem) SetCreatedByUser(value msmodel.Userable) { + m.createdByUser = value +} + +// SetCreatedDateTime sets the createdDateTime property value. Date and time of item creation. Read-only. +func (m *BaseItem) SetCreatedDateTime(value *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time) { + m.createdDateTime = value +} + +// SetDescription sets the description property value. The description property +func (m *BaseItem) SetDescription(value *string) { + m.description = value +} + +// SetETag sets the eTag property value. ETag for the item. Read-only. +func (m *BaseItem) SetETag(value *string) { + m.eTag = value +} + +// SetLastModifiedBy sets the lastModifiedBy property value. Identity of the user, device, and application which last modified the item. Read-only. +func (m *BaseItem) SetLastModifiedBy(value msmodel.IdentitySetable) { + m.lastModifiedBy = value +} + +// SetLastModifiedByUser sets the lastModifiedByUser property value. The lastModifiedByUser property +func (m *BaseItem) SetLastModifiedByUser(value msmodel.Userable) { + m.lastModifiedByUser = value +} + +// SetLastModifiedDateTime sets the lastModifiedDateTime property value. Date and time the item was last modified. Read-only. +func (m *BaseItem) SetLastModifiedDateTime(value *i336074805fc853987abe6f7fe3ad97a6a6f3077a16391fec744f671a015fbd7e.Time) { + m.lastModifiedDateTime = value +} + +// SetName sets the name property value. The name of the item. Read-write. +func (m *BaseItem) SetName(value *string) { + m.name = value +} + +// SetParentReference sets the parentReference property value. Parent information, if the item has a parent. Read-write. +func (m *BaseItem) SetParentReference(value msmodel.ItemReferenceable) { + m.parentReference = value +} + +// SetWebUrl sets the webUrl property value. URL that displays the resource in the browser. Read-only. +func (m *BaseItem) SetWebUrl(value *string) { + m.webUrl = value +} diff --git a/src/internal/connector/graph/betasdk/models/canvas_layout.go b/src/internal/connector/graph/betasdk/models/canvas_layout.go new file mode 100644 index 000000000..cda577a77 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/canvas_layout.go @@ -0,0 +1,103 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// CanvasLayout +type CanvasLayout struct { + msmodel.Entity + // Collection of horizontal sections on the SharePoint page. + horizontalSections []HorizontalSectionable + // Vertical section on the SharePoint page. + verticalSection VerticalSectionable +} + +// NewCanvasLayout instantiates a new canvasLayout and sets the default values. +func NewCanvasLayout() *CanvasLayout { + m := &CanvasLayout{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateCanvasLayoutFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateCanvasLayoutFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewCanvasLayout(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *CanvasLayout) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + res["horizontalSections"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateHorizontalSectionFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]HorizontalSectionable, len(val)) + for i, v := range val { + res[i] = v.(HorizontalSectionable) + } + m.SetHorizontalSections(res) + } + return nil + } + res["verticalSection"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateVerticalSectionFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetVerticalSection(val.(VerticalSectionable)) + } + return nil + } + return res +} + +// GetHorizontalSections gets the horizontalSections property value. Collection of horizontal sections on the SharePoint page. +func (m *CanvasLayout) GetHorizontalSections() []HorizontalSectionable { + return m.horizontalSections +} + +// GetVerticalSection gets the verticalSection property value. Vertical section on the SharePoint page. +func (m *CanvasLayout) GetVerticalSection() VerticalSectionable { + return m.verticalSection +} + +// Serialize serializes information the current object +func (m *CanvasLayout) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + if m.GetHorizontalSections() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetHorizontalSections())) + for i, v := range m.GetHorizontalSections() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("horizontalSections", cast) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("verticalSection", m.GetVerticalSection()) + if err != nil { + return err + } + } + return nil +} + +// SetHorizontalSections sets the horizontalSections property value. Collection of horizontal sections on the SharePoint page. +func (m *CanvasLayout) SetHorizontalSections(value []HorizontalSectionable) { + m.horizontalSections = value +} + +// SetVerticalSection sets the verticalSection property value. Vertical section on the SharePoint page. +func (m *CanvasLayout) SetVerticalSection(value VerticalSectionable) { + m.verticalSection = value +} diff --git a/src/internal/connector/graph/betasdk/models/canvas_layoutable.go b/src/internal/connector/graph/betasdk/models/canvas_layoutable.go new file mode 100644 index 000000000..537d5b0df --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/canvas_layoutable.go @@ -0,0 +1,16 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// CanvasLayoutable +type CanvasLayoutable interface { + msmodel.Entityable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetHorizontalSections() []HorizontalSectionable + GetVerticalSection() VerticalSectionable + SetHorizontalSections(value []HorizontalSectionable) + SetVerticalSection(value VerticalSectionable) +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section.go b/src/internal/connector/graph/betasdk/models/horizontal_section.go new file mode 100644 index 000000000..3e143d40f --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section.go @@ -0,0 +1,133 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSection provides operations to call the remove method. +type HorizontalSection struct { + msmodel.Entity + // The set of vertical columns in this section. + columns []HorizontalSectionColumnable + // Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. + emphasis *SectionEmphasisType + // Layout type of the section. The possible values are: none, oneColumn, twoColumns, threeColumns, oneThirdLeftColumn, oneThirdRightColumn, fullWidth, unknownFutureValue. + layout *HorizontalSectionLayoutType +} + +// NewHorizontalSection instantiates a new horizontalSection and sets the default values. +func NewHorizontalSection() *HorizontalSection { + m := &HorizontalSection{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateHorizontalSectionFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateHorizontalSectionFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewHorizontalSection(), nil +} + +// GetColumns gets the columns property value. The set of vertical columns in this section. +func (m *HorizontalSection) GetColumns() []HorizontalSectionColumnable { + return m.columns +} + +// GetEmphasis gets the emphasis property value. Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. +func (m *HorizontalSection) GetEmphasis() *SectionEmphasisType { + return m.emphasis +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *HorizontalSection) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + res["columns"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateHorizontalSectionColumnFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]HorizontalSectionColumnable, len(val)) + for i, v := range val { + res[i] = v.(HorizontalSectionColumnable) + } + m.SetColumns(res) + } + return nil + } + res["emphasis"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParseSectionEmphasisType) + if err != nil { + return err + } + if val != nil { + m.SetEmphasis(val.(*SectionEmphasisType)) + } + return nil + } + res["layout"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParseHorizontalSectionLayoutType) + if err != nil { + return err + } + if val != nil { + m.SetLayout(val.(*HorizontalSectionLayoutType)) + } + return nil + } + return res +} + +// GetLayout gets the layout property value. Layout type of the section. The possible values are: none, oneColumn, twoColumns, threeColumns, oneThirdLeftColumn, oneThirdRightColumn, fullWidth, unknownFutureValue. +func (m *HorizontalSection) GetLayout() *HorizontalSectionLayoutType { + return m.layout +} + +// Serialize serializes information the current object +func (m *HorizontalSection) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + if m.GetColumns() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetColumns())) + for i, v := range m.GetColumns() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("columns", cast) + if err != nil { + return err + } + } + if m.GetEmphasis() != nil { + cast := (*m.GetEmphasis()).String() + err = writer.WriteStringValue("emphasis", &cast) + if err != nil { + return err + } + } + if m.GetLayout() != nil { + cast := (*m.GetLayout()).String() + err = writer.WriteStringValue("layout", &cast) + if err != nil { + return err + } + } + return nil +} + +// SetColumns sets the columns property value. The set of vertical columns in this section. +func (m *HorizontalSection) SetColumns(value []HorizontalSectionColumnable) { + m.columns = value +} + +// SetEmphasis sets the emphasis property value. Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. +func (m *HorizontalSection) SetEmphasis(value *SectionEmphasisType) { + m.emphasis = value +} + +// SetLayout sets the layout property value. Layout type of the section. The possible values are: none, oneColumn, twoColumns, threeColumns, oneThirdLeftColumn, oneThirdRightColumn, fullWidth, unknownFutureValue. +func (m *HorizontalSection) SetLayout(value *HorizontalSectionLayoutType) { + m.layout = value +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_collection_response.go b/src/internal/connector/graph/betasdk/models/horizontal_section_collection_response.go new file mode 100644 index 000000000..70fb40959 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionCollectionResponse +type HorizontalSectionCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []HorizontalSectionable +} + +// NewHorizontalSectionCollectionResponse instantiates a new HorizontalSectionCollectionResponse and sets the default values. +func NewHorizontalSectionCollectionResponse() *HorizontalSectionCollectionResponse { + m := &HorizontalSectionCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateHorizontalSectionCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateHorizontalSectionCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewHorizontalSectionCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *HorizontalSectionCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateHorizontalSectionFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]HorizontalSectionable, len(val)) + for i, v := range val { + res[i] = v.(HorizontalSectionable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *HorizontalSectionCollectionResponse) GetValue() []HorizontalSectionable { + return m.value +} + +// Serialize serializes information the current object +func (m *HorizontalSectionCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *HorizontalSectionCollectionResponse) SetValue(value []HorizontalSectionable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_collection_responseable.go b/src/internal/connector/graph/betasdk/models/horizontal_section_collection_responseable.go new file mode 100644 index 000000000..a46d55060 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionCollectionResponseable +type HorizontalSectionCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []HorizontalSectionable + SetValue(value []HorizontalSectionable) +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_column.go b/src/internal/connector/graph/betasdk/models/horizontal_section_column.go new file mode 100644 index 000000000..f00261c16 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_column.go @@ -0,0 +1,103 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionColumn provides operations to call the remove method. +type HorizontalSectionColumn struct { + msmodel.Entity + // The collection of WebParts in this column. + webparts []WebPartable + // Width of the column. A horizontal section is divided into 12 grids. A column should have a value of 1-12 to represent its range spans. For example, there can be two columns both have a width of 6 in a section. + width *int32 +} + +// NewHorizontalSectionColumn instantiates a new horizontalSectionColumn and sets the default values. +func NewHorizontalSectionColumn() *HorizontalSectionColumn { + m := &HorizontalSectionColumn{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateHorizontalSectionColumnFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateHorizontalSectionColumnFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewHorizontalSectionColumn(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *HorizontalSectionColumn) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + res["webparts"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]WebPartable, len(val)) + for i, v := range val { + res[i] = v.(WebPartable) + } + m.SetWebparts(res) + } + return nil + } + res["width"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetWidth(val) + } + return nil + } + return res +} + +// GetWebparts gets the webparts property value. The collection of WebParts in this column. +func (m *HorizontalSectionColumn) GetWebparts() []WebPartable { + return m.webparts +} + +// GetWidth gets the width property value. Width of the column. A horizontal section is divided into 12 grids. A column should have a value of 1-12 to represent its range spans. For example, there can be two columns both have a width of 6 in a section. +func (m *HorizontalSectionColumn) GetWidth() *int32 { + return m.width +} + +// Serialize serializes information the current object +func (m *HorizontalSectionColumn) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + if m.GetWebparts() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetWebparts())) + for i, v := range m.GetWebparts() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("webparts", cast) + if err != nil { + return err + } + } + { + err = writer.WriteInt32Value("width", m.GetWidth()) + if err != nil { + return err + } + } + return nil +} + +// SetWebparts sets the webparts property value. The collection of WebParts in this column. +func (m *HorizontalSectionColumn) SetWebparts(value []WebPartable) { + m.webparts = value +} + +// SetWidth sets the width property value. Width of the column. A horizontal section is divided into 12 grids. A column should have a value of 1-12 to represent its range spans. For example, there can be two columns both have a width of 6 in a section. +func (m *HorizontalSectionColumn) SetWidth(value *int32) { + m.width = value +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_response.go b/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_response.go new file mode 100644 index 000000000..081244d8f --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionColumnCollectionResponse +type HorizontalSectionColumnCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []HorizontalSectionColumnable +} + +// NewHorizontalSectionColumnCollectionResponse instantiates a new HorizontalSectionColumnCollectionResponse and sets the default values. +func NewHorizontalSectionColumnCollectionResponse() *HorizontalSectionColumnCollectionResponse { + m := &HorizontalSectionColumnCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateHorizontalSectionColumnCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateHorizontalSectionColumnCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewHorizontalSectionColumnCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *HorizontalSectionColumnCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateHorizontalSectionColumnFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]HorizontalSectionColumnable, len(val)) + for i, v := range val { + res[i] = v.(HorizontalSectionColumnable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *HorizontalSectionColumnCollectionResponse) GetValue() []HorizontalSectionColumnable { + return m.value +} + +// Serialize serializes information the current object +func (m *HorizontalSectionColumnCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *HorizontalSectionColumnCollectionResponse) SetValue(value []HorizontalSectionColumnable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_responseable.go b/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_responseable.go new file mode 100644 index 000000000..b52c33b35 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_column_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionColumnCollectionResponseable +type HorizontalSectionColumnCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []HorizontalSectionColumnable + SetValue(value []HorizontalSectionColumnable) +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_columnable.go b/src/internal/connector/graph/betasdk/models/horizontal_section_columnable.go new file mode 100644 index 000000000..f4f185a4a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_columnable.go @@ -0,0 +1,16 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionColumnable +type HorizontalSectionColumnable interface { + msmodel.Entityable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetWebparts() []WebPartable + GetWidth() *int32 + SetWebparts(value []WebPartable) + SetWidth(value *int32) +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go b/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go new file mode 100644 index 000000000..43c2643a2 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go @@ -0,0 +1,52 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type HorizontalSectionLayoutType int + +const ( + NONE_HORIZONTALSECTIONLAYOUTTYPE HorizontalSectionLayoutType = iota + ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE + TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE + UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE +) + +func (i HorizontalSectionLayoutType) String() string { + return []string{"none", "oneColumn", "twoColumns", "threeColumns", "oneThirdLeftColumn", "oneThirdRightColumn", "fullWidth", "unknownFutureValue"}[i] +} +func ParseHorizontalSectionLayoutType(v string) (interface{}, error) { + result := NONE_HORIZONTALSECTIONLAYOUTTYPE + switch v { + case "none": + result = NONE_HORIZONTALSECTIONLAYOUTTYPE + case "oneColumn": + result = ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "twoColumns": + result = TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + case "threeColumns": + result = THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + case "oneThirdLeftColumn": + result = ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "oneThirdRightColumn": + result = ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "fullWidth": + result = FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE + default: + return 0, errors.New("Unknown HorizontalSectionLayoutType value: " + v) + } + return &result, nil +} +func SerializeHorizontalSectionLayoutType(values []HorizontalSectionLayoutType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/horizontal_sectionable.go b/src/internal/connector/graph/betasdk/models/horizontal_sectionable.go new file mode 100644 index 000000000..5ed926080 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/horizontal_sectionable.go @@ -0,0 +1,18 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// HorizontalSectionable +type HorizontalSectionable interface { + msmodel.Entityable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetColumns() []HorizontalSectionColumnable + GetEmphasis() *SectionEmphasisType + GetLayout() *HorizontalSectionLayoutType + SetColumns(value []HorizontalSectionColumnable) + SetEmphasis(value *SectionEmphasisType) + SetLayout(value *HorizontalSectionLayoutType) +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go new file mode 100644 index 000000000..e7df06165 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go @@ -0,0 +1,123 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// MetaDataKeyStringPair +type MetaDataKeyStringPair struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Key of the meta data. + key *string + // The OdataType property + odataType *string + // Value of the meta data. + value *string +} +// NewMetaDataKeyStringPair instantiates a new metaDataKeyStringPair and sets the default values. +func NewMetaDataKeyStringPair()(*MetaDataKeyStringPair) { + m := &MetaDataKeyStringPair{ + } + m.SetAdditionalData(make(map[string]interface{})); + return m +} +// CreateMetaDataKeyStringPairFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateMetaDataKeyStringPairFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewMetaDataKeyStringPair(), nil +} +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *MetaDataKeyStringPair) GetAdditionalData()(map[string]interface{}) { + return m.additionalData +} +// GetFieldDeserializers the deserialization information for the current model +func (m *MetaDataKeyStringPair) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) + res["key"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetKey(val) + } + return nil + } + res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["value"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetValue(val) + } + return nil + } + return res +} +// GetKey gets the key property value. Key of the meta data. +func (m *MetaDataKeyStringPair) GetKey()(*string) { + return m.key +} +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *MetaDataKeyStringPair) GetOdataType()(*string) { + return m.odataType +} +// GetValue gets the value property value. Value of the meta data. +func (m *MetaDataKeyStringPair) GetValue()(*string) { + return m.value +} +// Serialize serializes information the current object +func (m *MetaDataKeyStringPair) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + { + err := writer.WriteStringValue("key", m.GetKey()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("value", m.GetValue()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *MetaDataKeyStringPair) SetAdditionalData(value map[string]interface{})() { + m.additionalData = value +} +// SetKey sets the key property value. Key of the meta data. +func (m *MetaDataKeyStringPair) SetKey(value *string)() { + m.key = value +} +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *MetaDataKeyStringPair) SetOdataType(value *string)() { + m.odataType = value +} +// SetValue sets the value property value. Value of the meta data. +func (m *MetaDataKeyStringPair) SetValue(value *string)() { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_response.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_response.go new file mode 100644 index 000000000..b5c88ce49 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyStringPairCollectionResponse +type MetaDataKeyStringPairCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []MetaDataKeyStringPairable +} + +// NewMetaDataKeyStringPairCollectionResponse instantiates a new MetaDataKeyStringPairCollectionResponse and sets the default values. +func NewMetaDataKeyStringPairCollectionResponse() *MetaDataKeyStringPairCollectionResponse { + m := &MetaDataKeyStringPairCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateMetaDataKeyStringPairCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateMetaDataKeyStringPairCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewMetaDataKeyStringPairCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *MetaDataKeyStringPairCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *MetaDataKeyStringPairCollectionResponse) GetValue() []MetaDataKeyStringPairable { + return m.value +} + +// Serialize serializes information the current object +func (m *MetaDataKeyStringPairCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *MetaDataKeyStringPairCollectionResponse) SetValue(value []MetaDataKeyStringPairable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_responseable.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_responseable.go new file mode 100644 index 000000000..ad657d982 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyStringPairCollectionResponseable +type MetaDataKeyStringPairCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []MetaDataKeyStringPairable + SetValue(value []MetaDataKeyStringPairable) +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go new file mode 100644 index 000000000..49908469e --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go @@ -0,0 +1,17 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// MetaDataKeyStringPairable +type MetaDataKeyStringPairable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetKey()(*string) + GetOdataType()(*string) + GetValue()(*string) + SetKey(value *string)() + SetOdataType(value *string)() + SetValue(value *string)() +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair.go b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair.go new file mode 100644 index 000000000..a64ebdf0d --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair.go @@ -0,0 +1,135 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyValuePair +type MetaDataKeyValuePair struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Key of the metadata. + key *string + // The OdataType property + odataType *string + // Value of the metadata. Should be an object. + value msmodel.Jsonable +} + +// NewMetaDataKeyValuePair instantiates a new metaDataKeyValuePair and sets the default values. +func NewMetaDataKeyValuePair() *MetaDataKeyValuePair { + m := &MetaDataKeyValuePair{} + m.SetAdditionalData(make(map[string]interface{})) + return m +} + +// CreateMetaDataKeyValuePairFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateMetaDataKeyValuePairFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewMetaDataKeyValuePair(), nil +} + +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *MetaDataKeyValuePair) GetAdditionalData() map[string]interface{} { + return m.additionalData +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *MetaDataKeyValuePair) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["key"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetKey(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateJsonFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetValue(val.(msmodel.Jsonable)) + } + return nil + } + return res +} + +// GetKey gets the key property value. Key of the metadata. +func (m *MetaDataKeyValuePair) GetKey() *string { + return m.key +} + +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *MetaDataKeyValuePair) GetOdataType() *string { + return m.odataType +} + +// GetValue gets the value property value. Value of the metadata. Should be an object. +func (m *MetaDataKeyValuePair) GetValue() msmodel.Jsonable { + return m.value +} + +// Serialize serializes information the current object +func (m *MetaDataKeyValuePair) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteStringValue("key", m.GetKey()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteObjectValue("value", m.GetValue()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} + +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *MetaDataKeyValuePair) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value +} + +// SetKey sets the key property value. Key of the metadata. +func (m *MetaDataKeyValuePair) SetKey(value *string) { + m.key = value +} + +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *MetaDataKeyValuePair) SetOdataType(value *string) { + m.odataType = value +} + +// SetValue sets the value property value. Value of the metadata. Should be an object. +func (m *MetaDataKeyValuePair) SetValue(value msmodel.Jsonable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_response.go b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_response.go new file mode 100644 index 000000000..747cec59f --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyValuePairCollectionResponse +type MetaDataKeyValuePairCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []MetaDataKeyValuePairable +} + +// NewMetaDataKeyValuePairCollectionResponse instantiates a new MetaDataKeyValuePairCollectionResponse and sets the default values. +func NewMetaDataKeyValuePairCollectionResponse() *MetaDataKeyValuePairCollectionResponse { + m := &MetaDataKeyValuePairCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateMetaDataKeyValuePairCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateMetaDataKeyValuePairCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewMetaDataKeyValuePairCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *MetaDataKeyValuePairCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyValuePairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyValuePairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyValuePairable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *MetaDataKeyValuePairCollectionResponse) GetValue() []MetaDataKeyValuePairable { + return m.value +} + +// Serialize serializes information the current object +func (m *MetaDataKeyValuePairCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *MetaDataKeyValuePairCollectionResponse) SetValue(value []MetaDataKeyValuePairable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_responseable.go b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_responseable.go new file mode 100644 index 000000000..b2740c217 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pair_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyValuePairCollectionResponseable +type MetaDataKeyValuePairCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []MetaDataKeyValuePairable + SetValue(value []MetaDataKeyValuePairable) +} diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_value_pairable.go b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pairable.go new file mode 100644 index 000000000..e1bc31e74 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_value_pairable.go @@ -0,0 +1,18 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// MetaDataKeyValuePairable +type MetaDataKeyValuePairable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetKey() *string + GetOdataType() *string + GetValue() msmodel.Jsonable + SetKey(value *string) + SetOdataType(value *string) + SetValue(value msmodel.Jsonable) +} diff --git a/src/internal/connector/graph/betasdk/models/page_layout_type.go b/src/internal/connector/graph/betasdk/models/page_layout_type.go new file mode 100644 index 000000000..0338a5c30 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/page_layout_type.go @@ -0,0 +1,40 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type PageLayoutType int + +const ( + MICROSOFTRESERVED_PAGELAYOUTTYPE PageLayoutType = iota + ARTICLE_PAGELAYOUTTYPE + HOME_PAGELAYOUTTYPE + UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE +) + +func (i PageLayoutType) String() string { + return []string{"microsoftReserved", "article", "home", "unknownFutureValue"}[i] +} +func ParsePageLayoutType(v string) (interface{}, error) { + result := MICROSOFTRESERVED_PAGELAYOUTTYPE + switch v { + case "microsoftReserved": + result = MICROSOFTRESERVED_PAGELAYOUTTYPE + case "article": + result = ARTICLE_PAGELAYOUTTYPE + case "home": + result = HOME_PAGELAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE + default: + return 0, errors.New("Unknown PageLayoutType value: " + v) + } + return &result, nil +} +func SerializePageLayoutType(values []PageLayoutType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/page_promotion_type.go b/src/internal/connector/graph/betasdk/models/page_promotion_type.go new file mode 100644 index 000000000..a8cbcd058 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/page_promotion_type.go @@ -0,0 +1,40 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type PagePromotionType int + +const ( + MICROSOFTRESERVED_PAGEPROMOTIONTYPE PagePromotionType = iota + PAGE_PAGEPROMOTIONTYPE + NEWSPOST_PAGEPROMOTIONTYPE + UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE +) + +func (i PagePromotionType) String() string { + return []string{"microsoftReserved", "page", "newsPost", "unknownFutureValue"}[i] +} +func ParsePagePromotionType(v string) (interface{}, error) { + result := MICROSOFTRESERVED_PAGEPROMOTIONTYPE + switch v { + case "microsoftReserved": + result = MICROSOFTRESERVED_PAGEPROMOTIONTYPE + case "page": + result = PAGE_PAGEPROMOTIONTYPE + case "newsPost": + result = NEWSPOST_PAGEPROMOTIONTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE + default: + return 0, errors.New("Unknown PagePromotionType value: " + v) + } + return &result, nil +} +func SerializePagePromotionType(values []PagePromotionType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/publication_facet.go b/src/internal/connector/graph/betasdk/models/publication_facet.go new file mode 100644 index 000000000..87e59d34b --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/publication_facet.go @@ -0,0 +1,123 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// PublicationFacet +type PublicationFacet struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // The state of publication for this document. Either published or checkout. Read-only. + level *string + // The OdataType property + odataType *string + // The unique identifier for the version that is visible to the current caller. Read-only. + versionId *string +} +// NewPublicationFacet instantiates a new publicationFacet and sets the default values. +func NewPublicationFacet()(*PublicationFacet) { + m := &PublicationFacet{ + } + m.SetAdditionalData(make(map[string]interface{})); + return m +} +// CreatePublicationFacetFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreatePublicationFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewPublicationFacet(), nil +} +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *PublicationFacet) GetAdditionalData()(map[string]interface{}) { + return m.additionalData +} +// GetFieldDeserializers the deserialization information for the current model +func (m *PublicationFacet) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) + res["level"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetLevel(val) + } + return nil + } + res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["versionId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetVersionId(val) + } + return nil + } + return res +} +// GetLevel gets the level property value. The state of publication for this document. Either published or checkout. Read-only. +func (m *PublicationFacet) GetLevel()(*string) { + return m.level +} +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *PublicationFacet) GetOdataType()(*string) { + return m.odataType +} +// GetVersionId gets the versionId property value. The unique identifier for the version that is visible to the current caller. Read-only. +func (m *PublicationFacet) GetVersionId()(*string) { + return m.versionId +} +// Serialize serializes information the current object +func (m *PublicationFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + { + err := writer.WriteStringValue("level", m.GetLevel()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("versionId", m.GetVersionId()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *PublicationFacet) SetAdditionalData(value map[string]interface{})() { + m.additionalData = value +} +// SetLevel sets the level property value. The state of publication for this document. Either published or checkout. Read-only. +func (m *PublicationFacet) SetLevel(value *string)() { + m.level = value +} +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *PublicationFacet) SetOdataType(value *string)() { + m.odataType = value +} +// SetVersionId sets the versionId property value. The unique identifier for the version that is visible to the current caller. Read-only. +func (m *PublicationFacet) SetVersionId(value *string)() { + m.versionId = value +} diff --git a/src/internal/connector/graph/betasdk/models/publication_facetable.go b/src/internal/connector/graph/betasdk/models/publication_facetable.go new file mode 100644 index 000000000..20d82ccf8 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/publication_facetable.go @@ -0,0 +1,17 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// PublicationFacetable +type PublicationFacetable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetLevel()(*string) + GetOdataType()(*string) + GetVersionId()(*string) + SetLevel(value *string)() + SetOdataType(value *string)() + SetVersionId(value *string)() +} diff --git a/src/internal/connector/graph/betasdk/models/reactions_facet.go b/src/internal/connector/graph/betasdk/models/reactions_facet.go new file mode 100644 index 000000000..b298a9fe1 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/reactions_facet.go @@ -0,0 +1,149 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// ReactionsFacet +type ReactionsFacet struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Count of comments. + commentCount *int32 + // Count of likes. + likeCount *int32 + // The OdataType property + odataType *string + // Count of shares. + shareCount *int32 +} +// NewReactionsFacet instantiates a new reactionsFacet and sets the default values. +func NewReactionsFacet()(*ReactionsFacet) { + m := &ReactionsFacet{ + } + m.SetAdditionalData(make(map[string]interface{})); + return m +} +// CreateReactionsFacetFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateReactionsFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewReactionsFacet(), nil +} +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *ReactionsFacet) GetAdditionalData()(map[string]interface{}) { + return m.additionalData +} +// GetCommentCount gets the commentCount property value. Count of comments. +func (m *ReactionsFacet) GetCommentCount()(*int32) { + return m.commentCount +} +// GetFieldDeserializers the deserialization information for the current model +func (m *ReactionsFacet) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) + res["commentCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetCommentCount(val) + } + return nil + } + res["likeCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetLikeCount(val) + } + return nil + } + res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["shareCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetShareCount(val) + } + return nil + } + return res +} +// GetLikeCount gets the likeCount property value. Count of likes. +func (m *ReactionsFacet) GetLikeCount()(*int32) { + return m.likeCount +} +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *ReactionsFacet) GetOdataType()(*string) { + return m.odataType +} +// GetShareCount gets the shareCount property value. Count of shares. +func (m *ReactionsFacet) GetShareCount()(*int32) { + return m.shareCount +} +// Serialize serializes information the current object +func (m *ReactionsFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + { + err := writer.WriteInt32Value("commentCount", m.GetCommentCount()) + if err != nil { + return err + } + } + { + err := writer.WriteInt32Value("likeCount", m.GetLikeCount()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteInt32Value("shareCount", m.GetShareCount()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *ReactionsFacet) SetAdditionalData(value map[string]interface{})() { + m.additionalData = value +} +// SetCommentCount sets the commentCount property value. Count of comments. +func (m *ReactionsFacet) SetCommentCount(value *int32)() { + m.commentCount = value +} +// SetLikeCount sets the likeCount property value. Count of likes. +func (m *ReactionsFacet) SetLikeCount(value *int32)() { + m.likeCount = value +} +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *ReactionsFacet) SetOdataType(value *string)() { + m.odataType = value +} +// SetShareCount sets the shareCount property value. Count of shares. +func (m *ReactionsFacet) SetShareCount(value *int32)() { + m.shareCount = value +} diff --git a/src/internal/connector/graph/betasdk/models/reactions_facetable.go b/src/internal/connector/graph/betasdk/models/reactions_facetable.go new file mode 100644 index 000000000..4e5086047 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/reactions_facetable.go @@ -0,0 +1,19 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// ReactionsFacetable +type ReactionsFacetable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetCommentCount()(*int32) + GetLikeCount()(*int32) + GetOdataType()(*string) + GetShareCount()(*int32) + SetCommentCount(value *int32)() + SetLikeCount(value *int32)() + SetOdataType(value *string)() + SetShareCount(value *int32)() +} diff --git a/src/internal/connector/graph/betasdk/models/section_emphasis_type.go b/src/internal/connector/graph/betasdk/models/section_emphasis_type.go new file mode 100644 index 000000000..0016aec10 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/section_emphasis_type.go @@ -0,0 +1,43 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type SectionEmphasisType int + +const ( + NONE_SECTIONEMPHASISTYPE SectionEmphasisType = iota + NEUTRAL_SECTIONEMPHASISTYPE + SOFT_SECTIONEMPHASISTYPE + STRONG_SECTIONEMPHASISTYPE + UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE +) + +func (i SectionEmphasisType) String() string { + return []string{"none", "neutral", "soft", "strong", "unknownFutureValue"}[i] +} +func ParseSectionEmphasisType(v string) (interface{}, error) { + result := NONE_SECTIONEMPHASISTYPE + switch v { + case "none": + result = NONE_SECTIONEMPHASISTYPE + case "neutral": + result = NEUTRAL_SECTIONEMPHASISTYPE + case "soft": + result = SOFT_SECTIONEMPHASISTYPE + case "strong": + result = STRONG_SECTIONEMPHASISTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE + default: + return 0, errors.New("Unknown SectionEmphasisType value: " + v) + } + return &result, nil +} +func SerializeSectionEmphasisType(values []SectionEmphasisType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/server_processed_content.go b/src/internal/connector/graph/betasdk/models/server_processed_content.go new file mode 100644 index 000000000..572a5f87a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/server_processed_content.go @@ -0,0 +1,294 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// ServerProcessedContent +type ServerProcessedContent struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // A key-value map where keys are string identifiers and values are component ids. SharePoint servers might decide to use this hint to preload the script for corresponding components for performance boost. + componentDependencies []MetaDataKeyStringPairable + // A key-value map where keys are string identifier and values are object of custom key-value pair. + customMetadata []MetaDataKeyValuePairable + // A key-value map where keys are string identifiers and values are rich text with HTML format. SharePoint servers treat the values as HTML content and run services like safety checks, search index and link fixup on them. + htmlStrings []MetaDataKeyStringPairable + // A key-value map where keys are string identifiers and values are image sources. SharePoint servers treat the values as image sources and run services like search index and link fixup on them. + imageSources []MetaDataKeyStringPairable + // A key-value map where keys are string identifiers and values are links. SharePoint servers treat the values as links and run services like link fixup on them. + links []MetaDataKeyStringPairable + // The OdataType property + odataType *string + // A key-value map where keys are string identifiers and values are strings that should be search indexed. + searchablePlainTexts []MetaDataKeyStringPairable +} + +// NewServerProcessedContent instantiates a new serverProcessedContent and sets the default values. +func NewServerProcessedContent() *ServerProcessedContent { + m := &ServerProcessedContent{} + m.SetAdditionalData(make(map[string]interface{})) + return m +} + +// CreateServerProcessedContentFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateServerProcessedContentFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewServerProcessedContent(), nil +} + +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *ServerProcessedContent) GetAdditionalData() map[string]interface{} { + return m.additionalData +} + +// GetComponentDependencies gets the componentDependencies property value. A key-value map where keys are string identifiers and values are component ids. SharePoint servers might decide to use this hint to preload the script for corresponding components for performance boost. +func (m *ServerProcessedContent) GetComponentDependencies() []MetaDataKeyStringPairable { + return m.componentDependencies +} + +// GetCustomMetadata gets the customMetadata property value. A key-value map where keys are string identifier and values are object of custom key-value pair. +func (m *ServerProcessedContent) GetCustomMetadata() []MetaDataKeyValuePairable { + return m.customMetadata +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *ServerProcessedContent) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["componentDependencies"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetComponentDependencies(res) + } + return nil + } + res["customMetadata"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyValuePairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyValuePairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyValuePairable) + } + m.SetCustomMetadata(res) + } + return nil + } + res["htmlStrings"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetHtmlStrings(res) + } + return nil + } + res["imageSources"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetImageSources(res) + } + return nil + } + res["links"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetLinks(res) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["searchablePlainTexts"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateMetaDataKeyStringPairFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]MetaDataKeyStringPairable, len(val)) + for i, v := range val { + res[i] = v.(MetaDataKeyStringPairable) + } + m.SetSearchablePlainTexts(res) + } + return nil + } + return res +} + +// GetHtmlStrings gets the htmlStrings property value. A key-value map where keys are string identifiers and values are rich text with HTML format. SharePoint servers treat the values as HTML content and run services like safety checks, search index and link fixup on them. +func (m *ServerProcessedContent) GetHtmlStrings() []MetaDataKeyStringPairable { + return m.htmlStrings +} + +// GetImageSources gets the imageSources property value. A key-value map where keys are string identifiers and values are image sources. SharePoint servers treat the values as image sources and run services like search index and link fixup on them. +func (m *ServerProcessedContent) GetImageSources() []MetaDataKeyStringPairable { + return m.imageSources +} + +// GetLinks gets the links property value. A key-value map where keys are string identifiers and values are links. SharePoint servers treat the values as links and run services like link fixup on them. +func (m *ServerProcessedContent) GetLinks() []MetaDataKeyStringPairable { + return m.links +} + +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *ServerProcessedContent) GetOdataType() *string { + return m.odataType +} + +// GetSearchablePlainTexts gets the searchablePlainTexts property value. A key-value map where keys are string identifiers and values are strings that should be search indexed. +func (m *ServerProcessedContent) GetSearchablePlainTexts() []MetaDataKeyStringPairable { + return m.searchablePlainTexts +} + +// Serialize serializes information the current object +func (m *ServerProcessedContent) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + if m.GetComponentDependencies() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetComponentDependencies())) + for i, v := range m.GetComponentDependencies() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("componentDependencies", cast) + if err != nil { + return err + } + } + if m.GetCustomMetadata() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetCustomMetadata())) + for i, v := range m.GetCustomMetadata() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("customMetadata", cast) + if err != nil { + return err + } + } + if m.GetHtmlStrings() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetHtmlStrings())) + for i, v := range m.GetHtmlStrings() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("htmlStrings", cast) + if err != nil { + return err + } + } + if m.GetImageSources() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetImageSources())) + for i, v := range m.GetImageSources() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("imageSources", cast) + if err != nil { + return err + } + } + if m.GetLinks() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetLinks())) + for i, v := range m.GetLinks() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("links", cast) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + if m.GetSearchablePlainTexts() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetSearchablePlainTexts())) + for i, v := range m.GetSearchablePlainTexts() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err := writer.WriteCollectionOfObjectValues("searchablePlainTexts", cast) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} + +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *ServerProcessedContent) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value +} + +// SetComponentDependencies sets the componentDependencies property value. A key-value map where keys are string identifiers and values are component ids. SharePoint servers might decide to use this hint to preload the script for corresponding components for performance boost. +func (m *ServerProcessedContent) SetComponentDependencies(value []MetaDataKeyStringPairable) { + m.componentDependencies = value +} + +// SetCustomMetadata sets the customMetadata property value. A key-value map where keys are string identifier and values are object of custom key-value pair. +func (m *ServerProcessedContent) SetCustomMetadata(value []MetaDataKeyValuePairable) { + m.customMetadata = value +} + +// SetHtmlStrings sets the htmlStrings property value. A key-value map where keys are string identifiers and values are rich text with HTML format. SharePoint servers treat the values as HTML content and run services like safety checks, search index and link fixup on them. +func (m *ServerProcessedContent) SetHtmlStrings(value []MetaDataKeyStringPairable) { + m.htmlStrings = value +} + +// SetImageSources sets the imageSources property value. A key-value map where keys are string identifiers and values are image sources. SharePoint servers treat the values as image sources and run services like search index and link fixup on them. +func (m *ServerProcessedContent) SetImageSources(value []MetaDataKeyStringPairable) { + m.imageSources = value +} + +// SetLinks sets the links property value. A key-value map where keys are string identifiers and values are links. SharePoint servers treat the values as links and run services like link fixup on them. +func (m *ServerProcessedContent) SetLinks(value []MetaDataKeyStringPairable) { + m.links = value +} + +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *ServerProcessedContent) SetOdataType(value *string) { + m.odataType = value +} + +// SetSearchablePlainTexts sets the searchablePlainTexts property value. A key-value map where keys are string identifiers and values are strings that should be search indexed. +func (m *ServerProcessedContent) SetSearchablePlainTexts(value []MetaDataKeyStringPairable) { + m.searchablePlainTexts = value +} diff --git a/src/internal/connector/graph/betasdk/models/server_processed_contentable.go b/src/internal/connector/graph/betasdk/models/server_processed_contentable.go new file mode 100644 index 000000000..5f9faed1a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/server_processed_contentable.go @@ -0,0 +1,25 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// ServerProcessedContentable +type ServerProcessedContentable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetComponentDependencies() []MetaDataKeyStringPairable + GetCustomMetadata() []MetaDataKeyValuePairable + GetHtmlStrings() []MetaDataKeyStringPairable + GetImageSources() []MetaDataKeyStringPairable + GetLinks() []MetaDataKeyStringPairable + GetOdataType() *string + GetSearchablePlainTexts() []MetaDataKeyStringPairable + SetComponentDependencies(value []MetaDataKeyStringPairable) + SetCustomMetadata(value []MetaDataKeyValuePairable) + SetHtmlStrings(value []MetaDataKeyStringPairable) + SetImageSources(value []MetaDataKeyStringPairable) + SetLinks(value []MetaDataKeyStringPairable) + SetOdataType(value *string) + SetSearchablePlainTexts(value []MetaDataKeyStringPairable) +} diff --git a/src/internal/connector/graph/betasdk/models/site_access_type.go b/src/internal/connector/graph/betasdk/models/site_access_type.go new file mode 100644 index 000000000..052a2efdb --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_access_type.go @@ -0,0 +1,37 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type SiteAccessType int + +const ( + BLOCK_SITEACCESSTYPE SiteAccessType = iota + FULL_SITEACCESSTYPE + LIMITED_SITEACCESSTYPE +) + +func (i SiteAccessType) String() string { + return []string{"block", "full", "limited"}[i] +} +func ParseSiteAccessType(v string) (interface{}, error) { + result := BLOCK_SITEACCESSTYPE + switch v { + case "block": + result = BLOCK_SITEACCESSTYPE + case "full": + result = FULL_SITEACCESSTYPE + case "limited": + result = LIMITED_SITEACCESSTYPE + default: + return 0, errors.New("Unknown SiteAccessType value: " + v) + } + return &result, nil +} +func SerializeSiteAccessType(values []SiteAccessType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/site_page.go b/src/internal/connector/graph/betasdk/models/site_page.go new file mode 100644 index 000000000..7555fed52 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_page.go @@ -0,0 +1,387 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// SitePage provides operations to call the remove method. +type SitePage struct { + BaseItem + // Indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section + canvasLayout CanvasLayoutable + // Inherited from baseItem. + contentType msmodel.ContentTypeInfoable + // The name of the page layout of the page. The possible values are: microsoftReserved, article, home, unknownFutureValue. + pageLayout *PageLayoutType + // Indicates the promotion kind of the sitePage. The possible values are: microsoftReserved, page, newsPost, unknownFutureValue. + promotionKind *PagePromotionType + // The publishing status and the MM.mm version of the page. + publishingState PublicationFacetable + // Reactions information for the page. + reactions ReactionsFacetable + // Determines whether or not to show comments at the bottom of the page. + showComments *bool + // Determines whether or not to show recommended pages at the bottom of the page. + showRecommendedPages *bool + // Url of the sitePage's thumbnail image + thumbnailWebUrl *string + // Title of the sitePage. + title *string + // Title area on the SharePoint page. + titleArea TitleAreaable + // Collection of webparts on the SharePoint page + webParts []WebPartable +} + +// NewSitePage instantiates a new sitePage and sets the default values. +func NewSitePage() *SitePage { + m := &SitePage{ + BaseItem: *NewBaseItem(), + } + odataTypeValue := "#microsoft.graph.sitePage" + m.SetOdataType(&odataTypeValue) + return m +} + +// CreateSitePageFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateSitePageFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewSitePage(), nil +} + +// GetCanvasLayout gets the canvasLayout property value. Indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section +func (m *SitePage) GetCanvasLayout() CanvasLayoutable { + return m.canvasLayout +} + +// GetContentType gets the contentType property value. Inherited from baseItem. +func (m *SitePage) GetContentType() msmodel.ContentTypeInfoable { + return m.contentType +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *SitePage) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseItem.GetFieldDeserializers() + res["canvasLayout"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateCanvasLayoutFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetCanvasLayout(val.(CanvasLayoutable)) + } + return nil + } + res["contentType"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateContentTypeInfoFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetContentType(val.(msmodel.ContentTypeInfoable)) + } + return nil + } + res["pageLayout"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParsePageLayoutType) + if err != nil { + return err + } + if val != nil { + m.SetPageLayout(val.(*PageLayoutType)) + } + return nil + } + res["promotionKind"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParsePagePromotionType) + if err != nil { + return err + } + if val != nil { + m.SetPromotionKind(val.(*PagePromotionType)) + } + return nil + } + res["publishingState"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreatePublicationFacetFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetPublishingState(val.(PublicationFacetable)) + } + return nil + } + res["reactions"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateReactionsFacetFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetReactions(val.(ReactionsFacetable)) + } + return nil + } + res["showComments"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetShowComments(val) + } + return nil + } + res["showRecommendedPages"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetShowRecommendedPages(val) + } + return nil + } + res["thumbnailWebUrl"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetThumbnailWebUrl(val) + } + return nil + } + res["title"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetTitle(val) + } + return nil + } + res["titleArea"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateTitleAreaFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetTitleArea(val.(TitleAreaable)) + } + return nil + } + res["webParts"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]WebPartable, len(val)) + for i, v := range val { + res[i] = v.(WebPartable) + } + m.SetWebParts(res) + } + return nil + } + return res +} + +// GetPageLayout gets the pageLayout property value. The name of the page layout of the page. The possible values are: microsoftReserved, article, home, unknownFutureValue. +func (m *SitePage) GetPageLayout() *PageLayoutType { + return m.pageLayout +} + +// GetPromotionKind gets the promotionKind property value. Indicates the promotion kind of the sitePage. The possible values are: microsoftReserved, page, newsPost, unknownFutureValue. +func (m *SitePage) GetPromotionKind() *PagePromotionType { + return m.promotionKind +} + +// GetPublishingState gets the publishingState property value. The publishing status and the MM.mm version of the page. +func (m *SitePage) GetPublishingState() PublicationFacetable { + return m.publishingState +} + +// GetReactions gets the reactions property value. Reactions information for the page. +func (m *SitePage) GetReactions() ReactionsFacetable { + return m.reactions +} + +// GetShowComments gets the showComments property value. Determines whether or not to show comments at the bottom of the page. +func (m *SitePage) GetShowComments() *bool { + return m.showComments +} + +// GetShowRecommendedPages gets the showRecommendedPages property value. Determines whether or not to show recommended pages at the bottom of the page. +func (m *SitePage) GetShowRecommendedPages() *bool { + return m.showRecommendedPages +} + +// GetThumbnailWebUrl gets the thumbnailWebUrl property value. Url of the sitePage's thumbnail image +func (m *SitePage) GetThumbnailWebUrl() *string { + return m.thumbnailWebUrl +} + +// GetTitle gets the title property value. Title of the sitePage. +func (m *SitePage) GetTitle() *string { + return m.title +} + +// GetTitleArea gets the titleArea property value. Title area on the SharePoint page. +func (m *SitePage) GetTitleArea() TitleAreaable { + return m.titleArea +} + +// GetWebParts gets the webParts property value. Collection of webparts on the SharePoint page +func (m *SitePage) GetWebParts() []WebPartable { + return m.webParts +} + +// Serialize serializes information the current object +func (m *SitePage) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseItem.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteObjectValue("canvasLayout", m.GetCanvasLayout()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("contentType", m.GetContentType()) + if err != nil { + return err + } + } + if m.GetPageLayout() != nil { + cast := (*m.GetPageLayout()).String() + err = writer.WriteStringValue("pageLayout", &cast) + if err != nil { + return err + } + } + if m.GetPromotionKind() != nil { + cast := (*m.GetPromotionKind()).String() + err = writer.WriteStringValue("promotionKind", &cast) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("publishingState", m.GetPublishingState()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("reactions", m.GetReactions()) + if err != nil { + return err + } + } + { + err = writer.WriteBoolValue("showComments", m.GetShowComments()) + if err != nil { + return err + } + } + { + err = writer.WriteBoolValue("showRecommendedPages", m.GetShowRecommendedPages()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("thumbnailWebUrl", m.GetThumbnailWebUrl()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("title", m.GetTitle()) + if err != nil { + return err + } + } + { + err = writer.WriteObjectValue("titleArea", m.GetTitleArea()) + if err != nil { + return err + } + } + if m.GetWebParts() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetWebParts())) + for i, v := range m.GetWebParts() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("webParts", cast) + if err != nil { + return err + } + } + return nil +} + +// SetCanvasLayout sets the canvasLayout property value. Indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section +func (m *SitePage) SetCanvasLayout(value CanvasLayoutable) { + m.canvasLayout = value +} + +// SetContentType sets the contentType property value. Inherited from baseItem. +func (m *SitePage) SetContentType(value msmodel.ContentTypeInfoable) { + m.contentType = value +} + +// SetPageLayout sets the pageLayout property value. The name of the page layout of the page. The possible values are: microsoftReserved, article, home, unknownFutureValue. +func (m *SitePage) SetPageLayout(value *PageLayoutType) { + m.pageLayout = value +} + +// SetPromotionKind sets the promotionKind property value. Indicates the promotion kind of the sitePage. The possible values are: microsoftReserved, page, newsPost, unknownFutureValue. +func (m *SitePage) SetPromotionKind(value *PagePromotionType) { + m.promotionKind = value +} + +// SetPublishingState sets the publishingState property value. The publishing status and the MM.mm version of the page. +func (m *SitePage) SetPublishingState(value PublicationFacetable) { + m.publishingState = value +} + +// SetReactions sets the reactions property value. Reactions information for the page. +func (m *SitePage) SetReactions(value ReactionsFacetable) { + m.reactions = value +} + +// SetShowComments sets the showComments property value. Determines whether or not to show comments at the bottom of the page. +func (m *SitePage) SetShowComments(value *bool) { + m.showComments = value +} + +// SetShowRecommendedPages sets the showRecommendedPages property value. Determines whether or not to show recommended pages at the bottom of the page. +func (m *SitePage) SetShowRecommendedPages(value *bool) { + m.showRecommendedPages = value +} + +// SetThumbnailWebUrl sets the thumbnailWebUrl property value. Url of the sitePage's thumbnail image +func (m *SitePage) SetThumbnailWebUrl(value *string) { + m.thumbnailWebUrl = value +} + +// SetTitle sets the title property value. Title of the sitePage. +func (m *SitePage) SetTitle(value *string) { + m.title = value +} + +// SetTitleArea sets the titleArea property value. Title area on the SharePoint page. +func (m *SitePage) SetTitleArea(value TitleAreaable) { + m.titleArea = value +} + +// SetWebParts sets the webParts property value. Collection of webparts on the SharePoint page +func (m *SitePage) SetWebParts(value []WebPartable) { + m.webParts = value +} diff --git a/src/internal/connector/graph/betasdk/models/site_page_collection_response.go b/src/internal/connector/graph/betasdk/models/site_page_collection_response.go new file mode 100644 index 000000000..f66cdafdf --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_page_collection_response.go @@ -0,0 +1,76 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// SitePageCollectionResponse provides operations to manage the pages property of the microsoft.graph.site entity. +type SitePageCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []SitePageable +} + +// NewSitePageCollectionResponse instantiates a new SitePageCollectionResponse and sets the default values. +func NewSitePageCollectionResponse() *SitePageCollectionResponse { + m := &SitePageCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateSitePageCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateSitePageCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewSitePageCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *SitePageCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateSitePageFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]SitePageable, len(val)) + for i, v := range val { + res[i] = v.(SitePageable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *SitePageCollectionResponse) GetValue() []SitePageable { + return m.value +} + +// Serialize serializes information the current object +func (m *SitePageCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *SitePageCollectionResponse) SetValue(value []SitePageable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/site_page_collection_responseable.go b/src/internal/connector/graph/betasdk/models/site_page_collection_responseable.go new file mode 100644 index 000000000..30cceeb11 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_page_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// SitePageCollectionResponseable +type SitePageCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []SitePageable + SetValue(value []SitePageable) +} diff --git a/src/internal/connector/graph/betasdk/models/site_pageable.go b/src/internal/connector/graph/betasdk/models/site_pageable.go new file mode 100644 index 000000000..1131bfa9d --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_pageable.go @@ -0,0 +1,36 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// SitePageable +type SitePageable interface { + msmodel.BaseItemable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetCanvasLayout() CanvasLayoutable + GetContentType() msmodel.ContentTypeInfoable + GetPageLayout() *PageLayoutType + GetPromotionKind() *PagePromotionType + GetPublishingState() PublicationFacetable + GetReactions() ReactionsFacetable + GetShowComments() *bool + GetShowRecommendedPages() *bool + GetThumbnailWebUrl() *string + GetTitle() *string + GetTitleArea() TitleAreaable + GetWebParts() []WebPartable + SetCanvasLayout(value CanvasLayoutable) + SetContentType(value msmodel.ContentTypeInfoable) + SetPageLayout(value *PageLayoutType) + SetPromotionKind(value *PagePromotionType) + SetPublishingState(value PublicationFacetable) + SetReactions(value ReactionsFacetable) + SetShowComments(value *bool) + SetShowRecommendedPages(value *bool) + SetThumbnailWebUrl(value *string) + SetTitle(value *string) + SetTitleArea(value TitleAreaable) + SetWebParts(value []WebPartable) +} diff --git a/src/internal/connector/graph/betasdk/models/site_security_level.go b/src/internal/connector/graph/betasdk/models/site_security_level.go new file mode 100644 index 000000000..d2733ce47 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_security_level.go @@ -0,0 +1,52 @@ +package models +import ( + "errors" +) +// Provides operations to call the add method. +type SiteSecurityLevel int + +const ( + // User Defined, default value, no intent. + USERDEFINED_SITESECURITYLEVEL SiteSecurityLevel = iota + // Low. + LOW_SITESECURITYLEVEL + // Medium-low. + MEDIUMLOW_SITESECURITYLEVEL + // Medium. + MEDIUM_SITESECURITYLEVEL + // Medium-high. + MEDIUMHIGH_SITESECURITYLEVEL + // High. + HIGH_SITESECURITYLEVEL +) + +func (i SiteSecurityLevel) String() string { + return []string{"userDefined", "low", "mediumLow", "medium", "mediumHigh", "high"}[i] +} +func ParseSiteSecurityLevel(v string) (interface{}, error) { + result := USERDEFINED_SITESECURITYLEVEL + switch v { + case "userDefined": + result = USERDEFINED_SITESECURITYLEVEL + case "low": + result = LOW_SITESECURITYLEVEL + case "mediumLow": + result = MEDIUMLOW_SITESECURITYLEVEL + case "medium": + result = MEDIUM_SITESECURITYLEVEL + case "mediumHigh": + result = MEDIUMHIGH_SITESECURITYLEVEL + case "high": + result = HIGH_SITESECURITYLEVEL + default: + return 0, errors.New("Unknown SiteSecurityLevel value: " + v) + } + return &result, nil +} +func SerializeSiteSecurityLevel(values []SiteSecurityLevel) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/site_settings.go b/src/internal/connector/graph/betasdk/models/site_settings.go new file mode 100644 index 000000000..a2a36d94a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_settings.go @@ -0,0 +1,123 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// SiteSettings +type SiteSettings struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // The language tag for the language used on this site. + languageTag *string + // The OdataType property + odataType *string + // Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). + timeZone *string +} +// NewSiteSettings instantiates a new siteSettings and sets the default values. +func NewSiteSettings()(*SiteSettings) { + m := &SiteSettings{ + } + m.SetAdditionalData(make(map[string]interface{})); + return m +} +// CreateSiteSettingsFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateSiteSettingsFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewSiteSettings(), nil +} +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *SiteSettings) GetAdditionalData()(map[string]interface{}) { + return m.additionalData +} +// GetFieldDeserializers the deserialization information for the current model +func (m *SiteSettings) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) + res["languageTag"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetLanguageTag(val) + } + return nil + } + res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["timeZone"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetTimeZone(val) + } + return nil + } + return res +} +// GetLanguageTag gets the languageTag property value. The language tag for the language used on this site. +func (m *SiteSettings) GetLanguageTag()(*string) { + return m.languageTag +} +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *SiteSettings) GetOdataType()(*string) { + return m.odataType +} +// GetTimeZone gets the timeZone property value. Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). +func (m *SiteSettings) GetTimeZone()(*string) { + return m.timeZone +} +// Serialize serializes information the current object +func (m *SiteSettings) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + { + err := writer.WriteStringValue("languageTag", m.GetLanguageTag()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("timeZone", m.GetTimeZone()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *SiteSettings) SetAdditionalData(value map[string]interface{})() { + m.additionalData = value +} +// SetLanguageTag sets the languageTag property value. The language tag for the language used on this site. +func (m *SiteSettings) SetLanguageTag(value *string)() { + m.languageTag = value +} +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *SiteSettings) SetOdataType(value *string)() { + m.odataType = value +} +// SetTimeZone sets the timeZone property value. Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). +func (m *SiteSettings) SetTimeZone(value *string)() { + m.timeZone = value +} diff --git a/src/internal/connector/graph/betasdk/models/site_settingsable.go b/src/internal/connector/graph/betasdk/models/site_settingsable.go new file mode 100644 index 000000000..0423550ea --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/site_settingsable.go @@ -0,0 +1,17 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// SiteSettingsable +type SiteSettingsable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetLanguageTag()(*string) + GetOdataType()(*string) + GetTimeZone()(*string) + SetLanguageTag(value *string)() + SetOdataType(value *string)() + SetTimeZone(value *string)() +} diff --git a/src/internal/connector/graph/betasdk/models/standard_web_part.go b/src/internal/connector/graph/betasdk/models/standard_web_part.go new file mode 100644 index 000000000..0b7b4427a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/standard_web_part.go @@ -0,0 +1,88 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// StandardWebPart +type StandardWebPart struct { + WebPart + // Data of the webPart. + data WebPartDataable + // A Guid which indicates the type of the webParts + webPartType *string +} +// NewStandardWebPart instantiates a new StandardWebPart and sets the default values. +func NewStandardWebPart()(*StandardWebPart) { + m := &StandardWebPart{ + WebPart: *NewWebPart(), + } + odataTypeValue := "#microsoft.graph.standardWebPart"; + m.SetOdataType(&odataTypeValue); + return m +} +// CreateStandardWebPartFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateStandardWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewStandardWebPart(), nil +} +// GetData gets the data property value. Data of the webPart. +func (m *StandardWebPart) GetData()(WebPartDataable) { + return m.data +} +// GetFieldDeserializers the deserialization information for the current model +func (m *StandardWebPart) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := m.WebPart.GetFieldDeserializers() + res["data"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateWebPartDataFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetData(val.(WebPartDataable)) + } + return nil + } + res["webPartType"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetWebPartType(val) + } + return nil + } + return res +} +// GetWebPartType gets the webPartType property value. A Guid which indicates the type of the webParts +func (m *StandardWebPart) GetWebPartType()(*string) { + return m.webPartType +} +// Serialize serializes information the current object +func (m *StandardWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + err := m.WebPart.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteObjectValue("data", m.GetData()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("webPartType", m.GetWebPartType()) + if err != nil { + return err + } + } + return nil +} +// SetData sets the data property value. Data of the webPart. +func (m *StandardWebPart) SetData(value WebPartDataable)() { + m.data = value +} +// SetWebPartType sets the webPartType property value. A Guid which indicates the type of the webParts +func (m *StandardWebPart) SetWebPartType(value *string)() { + m.webPartType = value +} diff --git a/src/internal/connector/graph/betasdk/models/standard_web_part_collection_response.go b/src/internal/connector/graph/betasdk/models/standard_web_part_collection_response.go new file mode 100644 index 000000000..084496a8a --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/standard_web_part_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// StandardWebPartCollectionResponse +type StandardWebPartCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []StandardWebPartable +} + +// NewStandardWebPartCollectionResponse instantiates a new StandardWebPartCollectionResponse and sets the default values. +func NewStandardWebPartCollectionResponse() *StandardWebPartCollectionResponse { + m := &StandardWebPartCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateStandardWebPartCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateStandardWebPartCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewStandardWebPartCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *StandardWebPartCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateStandardWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]StandardWebPartable, len(val)) + for i, v := range val { + res[i] = v.(StandardWebPartable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *StandardWebPartCollectionResponse) GetValue() []StandardWebPartable { + return m.value +} + +// Serialize serializes information the current object +func (m *StandardWebPartCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *StandardWebPartCollectionResponse) SetValue(value []StandardWebPartable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/standard_web_part_collection_responseable.go b/src/internal/connector/graph/betasdk/models/standard_web_part_collection_responseable.go new file mode 100644 index 000000000..9e1f4d0d7 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/standard_web_part_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// StandardWebPartCollectionResponseable +type StandardWebPartCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []StandardWebPartable + SetValue(value []StandardWebPartable) +} diff --git a/src/internal/connector/graph/betasdk/models/standard_web_partable.go b/src/internal/connector/graph/betasdk/models/standard_web_partable.go new file mode 100644 index 000000000..e09160b2b --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/standard_web_partable.go @@ -0,0 +1,15 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// StandardWebPartable +type StandardWebPartable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + WebPartable + GetData()(WebPartDataable) + GetWebPartType()(*string) + SetData(value WebPartDataable)() + SetWebPartType(value *string)() +} diff --git a/src/internal/connector/graph/betasdk/models/text_web_part.go b/src/internal/connector/graph/betasdk/models/text_web_part.go new file mode 100644 index 000000000..f607ffa31 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/text_web_part.go @@ -0,0 +1,62 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// TextWebPart +type TextWebPart struct { + WebPart + // The HTML string in text web part. + innerHtml *string +} +// NewTextWebPart instantiates a new TextWebPart and sets the default values. +func NewTextWebPart()(*TextWebPart) { + m := &TextWebPart{ + WebPart: *NewWebPart(), + } + odataTypeValue := "#microsoft.graph.textWebPart"; + m.SetOdataType(&odataTypeValue); + return m +} +// CreateTextWebPartFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateTextWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewTextWebPart(), nil +} +// GetFieldDeserializers the deserialization information for the current model +func (m *TextWebPart) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := m.WebPart.GetFieldDeserializers() + res["innerHtml"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetInnerHtml(val) + } + return nil + } + return res +} +// GetInnerHtml gets the innerHtml property value. The HTML string in text web part. +func (m *TextWebPart) GetInnerHtml()(*string) { + return m.innerHtml +} +// Serialize serializes information the current object +func (m *TextWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + err := m.WebPart.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteStringValue("innerHtml", m.GetInnerHtml()) + if err != nil { + return err + } + } + return nil +} +// SetInnerHtml sets the innerHtml property value. The HTML string in text web part. +func (m *TextWebPart) SetInnerHtml(value *string)() { + m.innerHtml = value +} diff --git a/src/internal/connector/graph/betasdk/models/text_web_part_collection_response.go b/src/internal/connector/graph/betasdk/models/text_web_part_collection_response.go new file mode 100644 index 000000000..ea07beae7 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/text_web_part_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// TextWebPartCollectionResponse +type TextWebPartCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []TextWebPartable +} + +// NewTextWebPartCollectionResponse instantiates a new TextWebPartCollectionResponse and sets the default values. +func NewTextWebPartCollectionResponse() *TextWebPartCollectionResponse { + m := &TextWebPartCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateTextWebPartCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateTextWebPartCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewTextWebPartCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *TextWebPartCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateTextWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]TextWebPartable, len(val)) + for i, v := range val { + res[i] = v.(TextWebPartable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *TextWebPartCollectionResponse) GetValue() []TextWebPartable { + return m.value +} + +// Serialize serializes information the current object +func (m *TextWebPartCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *TextWebPartCollectionResponse) SetValue(value []TextWebPartable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/text_web_part_collection_responseable.go b/src/internal/connector/graph/betasdk/models/text_web_part_collection_responseable.go new file mode 100644 index 000000000..785618756 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/text_web_part_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// TextWebPartCollectionResponseable +type TextWebPartCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []TextWebPartable + SetValue(value []TextWebPartable) +} diff --git a/src/internal/connector/graph/betasdk/models/text_web_partable.go b/src/internal/connector/graph/betasdk/models/text_web_partable.go new file mode 100644 index 000000000..45e21d92b --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/text_web_partable.go @@ -0,0 +1,13 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// TextWebPartable +type TextWebPartable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + WebPartable + GetInnerHtml()(*string) + SetInnerHtml(value *string)() +} diff --git a/src/internal/connector/graph/betasdk/models/title_area.go b/src/internal/connector/graph/betasdk/models/title_area.go new file mode 100644 index 000000000..77b61ec7c --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/title_area.go @@ -0,0 +1,360 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// TitleArea +type TitleArea struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Alternative text on the title area. + alternativeText *string + // Indicates whether the title area has a gradient effect enabled. + enableGradientEffect *bool + // URL of the image in the title area. + imageWebUrl *string + // Enumeration value that indicates the layout of the title area. The possible values are: imageAndTitle, plain, colorBlock, overlap, unknownFutureValue. + layout *TitleAreaLayoutType + // The OdataType property + odataType *string + // Contains collections of data that can be processed by server side services like search index and link fixup. + serverProcessedContent ServerProcessedContentable + // Indicates whether the author should be shown in title area. + showAuthor *bool + // Indicates whether the published date should be shown in title area. + showPublishedDate *bool + // Indicates whether the text block above title should be shown in title area. + showTextBlockAboveTitle *bool + // The text above title line. + textAboveTitle *string + // Enumeration value that indicates the text alignment of the title area. The possible values are: left, center, unknownFutureValue. + textAlignment *TitleAreaTextAlignmentType +} + +// NewTitleArea instantiates a new titleArea and sets the default values. +func NewTitleArea() *TitleArea { + m := &TitleArea{} + m.SetAdditionalData(make(map[string]interface{})) + return m +} + +// CreateTitleAreaFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateTitleAreaFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewTitleArea(), nil +} + +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *TitleArea) GetAdditionalData() map[string]interface{} { + return m.additionalData +} + +// GetAlternativeText gets the alternativeText property value. Alternative text on the title area. +func (m *TitleArea) GetAlternativeText() *string { + return m.alternativeText +} + +// GetEnableGradientEffect gets the enableGradientEffect property value. Indicates whether the title area has a gradient effect enabled. +func (m *TitleArea) GetEnableGradientEffect() *bool { + return m.enableGradientEffect +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *TitleArea) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["alternativeText"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetAlternativeText(val) + } + return nil + } + res["enableGradientEffect"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetEnableGradientEffect(val) + } + return nil + } + res["imageWebUrl"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetImageWebUrl(val) + } + return nil + } + res["layout"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParseTitleAreaLayoutType) + if err != nil { + return err + } + if val != nil { + m.SetLayout(val.(*TitleAreaLayoutType)) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["serverProcessedContent"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateServerProcessedContentFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetServerProcessedContent(val.(ServerProcessedContentable)) + } + return nil + } + res["showAuthor"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetShowAuthor(val) + } + return nil + } + res["showPublishedDate"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetShowPublishedDate(val) + } + return nil + } + res["showTextBlockAboveTitle"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetShowTextBlockAboveTitle(val) + } + return nil + } + res["textAboveTitle"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetTextAboveTitle(val) + } + return nil + } + res["textAlignment"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParseTitleAreaTextAlignmentType) + if err != nil { + return err + } + if val != nil { + m.SetTextAlignment(val.(*TitleAreaTextAlignmentType)) + } + return nil + } + return res +} + +// GetImageWebUrl gets the imageWebUrl property value. URL of the image in the title area. +func (m *TitleArea) GetImageWebUrl() *string { + return m.imageWebUrl +} + +// GetLayout gets the layout property value. Enumeration value that indicates the layout of the title area. The possible values are: imageAndTitle, plain, colorBlock, overlap, unknownFutureValue. +func (m *TitleArea) GetLayout() *TitleAreaLayoutType { + return m.layout +} + +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *TitleArea) GetOdataType() *string { + return m.odataType +} + +// GetServerProcessedContent gets the serverProcessedContent property value. Contains collections of data that can be processed by server side services like search index and link fixup. +func (m *TitleArea) GetServerProcessedContent() ServerProcessedContentable { + return m.serverProcessedContent +} + +// GetShowAuthor gets the showAuthor property value. Indicates whether the author should be shown in title area. +func (m *TitleArea) GetShowAuthor() *bool { + return m.showAuthor +} + +// GetShowPublishedDate gets the showPublishedDate property value. Indicates whether the published date should be shown in title area. +func (m *TitleArea) GetShowPublishedDate() *bool { + return m.showPublishedDate +} + +// GetShowTextBlockAboveTitle gets the showTextBlockAboveTitle property value. Indicates whether the text block above title should be shown in title area. +func (m *TitleArea) GetShowTextBlockAboveTitle() *bool { + return m.showTextBlockAboveTitle +} + +// GetTextAboveTitle gets the textAboveTitle property value. The text above title line. +func (m *TitleArea) GetTextAboveTitle() *string { + return m.textAboveTitle +} + +// GetTextAlignment gets the textAlignment property value. Enumeration value that indicates the text alignment of the title area. The possible values are: left, center, unknownFutureValue. +func (m *TitleArea) GetTextAlignment() *TitleAreaTextAlignmentType { + return m.textAlignment +} + +// Serialize serializes information the current object +func (m *TitleArea) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteStringValue("alternativeText", m.GetAlternativeText()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("enableGradientEffect", m.GetEnableGradientEffect()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("imageWebUrl", m.GetImageWebUrl()) + if err != nil { + return err + } + } + if m.GetLayout() != nil { + cast := (*m.GetLayout()).String() + err := writer.WriteStringValue("layout", &cast) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteObjectValue("serverProcessedContent", m.GetServerProcessedContent()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("showAuthor", m.GetShowAuthor()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("showPublishedDate", m.GetShowPublishedDate()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("showTextBlockAboveTitle", m.GetShowTextBlockAboveTitle()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("textAboveTitle", m.GetTextAboveTitle()) + if err != nil { + return err + } + } + if m.GetTextAlignment() != nil { + cast := (*m.GetTextAlignment()).String() + err := writer.WriteStringValue("textAlignment", &cast) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} + +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *TitleArea) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value +} + +// SetAlternativeText sets the alternativeText property value. Alternative text on the title area. +func (m *TitleArea) SetAlternativeText(value *string) { + m.alternativeText = value +} + +// SetEnableGradientEffect sets the enableGradientEffect property value. Indicates whether the title area has a gradient effect enabled. +func (m *TitleArea) SetEnableGradientEffect(value *bool) { + m.enableGradientEffect = value +} + +// SetImageWebUrl sets the imageWebUrl property value. URL of the image in the title area. +func (m *TitleArea) SetImageWebUrl(value *string) { + m.imageWebUrl = value +} + +// SetLayout sets the layout property value. Enumeration value that indicates the layout of the title area. The possible values are: imageAndTitle, plain, colorBlock, overlap, unknownFutureValue. +func (m *TitleArea) SetLayout(value *TitleAreaLayoutType) { + m.layout = value +} + +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *TitleArea) SetOdataType(value *string) { + m.odataType = value +} + +// SetServerProcessedContent sets the serverProcessedContent property value. Contains collections of data that can be processed by server side services like search index and link fixup. +func (m *TitleArea) SetServerProcessedContent(value ServerProcessedContentable) { + m.serverProcessedContent = value +} + +// SetShowAuthor sets the showAuthor property value. Indicates whether the author should be shown in title area. +func (m *TitleArea) SetShowAuthor(value *bool) { + m.showAuthor = value +} + +// SetShowPublishedDate sets the showPublishedDate property value. Indicates whether the published date should be shown in title area. +func (m *TitleArea) SetShowPublishedDate(value *bool) { + m.showPublishedDate = value +} + +// SetShowTextBlockAboveTitle sets the showTextBlockAboveTitle property value. Indicates whether the text block above title should be shown in title area. +func (m *TitleArea) SetShowTextBlockAboveTitle(value *bool) { + m.showTextBlockAboveTitle = value +} + +// SetTextAboveTitle sets the textAboveTitle property value. The text above title line. +func (m *TitleArea) SetTextAboveTitle(value *string) { + m.textAboveTitle = value +} + +// SetTextAlignment sets the textAlignment property value. Enumeration value that indicates the text alignment of the title area. The possible values are: left, center, unknownFutureValue. +func (m *TitleArea) SetTextAlignment(value *TitleAreaTextAlignmentType) { + m.textAlignment = value +} diff --git a/src/internal/connector/graph/betasdk/models/title_area_layout_type.go b/src/internal/connector/graph/betasdk/models/title_area_layout_type.go new file mode 100644 index 000000000..375b68874 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/title_area_layout_type.go @@ -0,0 +1,43 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type TitleAreaLayoutType int + +const ( + IMAGEANDTITLE_TITLEAREALAYOUTTYPE TitleAreaLayoutType = iota + PLAIN_TITLEAREALAYOUTTYPE + COLORBLOCK_TITLEAREALAYOUTTYPE + OVERLAP_TITLEAREALAYOUTTYPE + UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE +) + +func (i TitleAreaLayoutType) String() string { + return []string{"imageAndTitle", "plain", "colorBlock", "overlap", "unknownFutureValue"}[i] +} +func ParseTitleAreaLayoutType(v string) (interface{}, error) { + result := IMAGEANDTITLE_TITLEAREALAYOUTTYPE + switch v { + case "imageAndTitle": + result = IMAGEANDTITLE_TITLEAREALAYOUTTYPE + case "plain": + result = PLAIN_TITLEAREALAYOUTTYPE + case "colorBlock": + result = COLORBLOCK_TITLEAREALAYOUTTYPE + case "overlap": + result = OVERLAP_TITLEAREALAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE + default: + return 0, errors.New("Unknown TitleAreaLayoutType value: " + v) + } + return &result, nil +} +func SerializeTitleAreaLayoutType(values []TitleAreaLayoutType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go b/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go new file mode 100644 index 000000000..27b1e1dba --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go @@ -0,0 +1,37 @@ +package models +import ( + "errors" +) +// Provides operations to call the remove method. +type TitleAreaTextAlignmentType int + +const ( + LEFT_TITLEAREATEXTALIGNMENTTYPE TitleAreaTextAlignmentType = iota + CENTER_TITLEAREATEXTALIGNMENTTYPE + UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE +) + +func (i TitleAreaTextAlignmentType) String() string { + return []string{"left", "center", "unknownFutureValue"}[i] +} +func ParseTitleAreaTextAlignmentType(v string) (interface{}, error) { + result := LEFT_TITLEAREATEXTALIGNMENTTYPE + switch v { + case "left": + result = LEFT_TITLEAREATEXTALIGNMENTTYPE + case "center": + result = CENTER_TITLEAREATEXTALIGNMENTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE + default: + return 0, errors.New("Unknown TitleAreaTextAlignmentType value: " + v) + } + return &result, nil +} +func SerializeTitleAreaTextAlignmentType(values []TitleAreaTextAlignmentType) []string { + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result +} diff --git a/src/internal/connector/graph/betasdk/models/title_areaable.go b/src/internal/connector/graph/betasdk/models/title_areaable.go new file mode 100644 index 000000000..fb36bf576 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/title_areaable.go @@ -0,0 +1,33 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// TitleAreaable +type TitleAreaable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetAlternativeText() *string + GetEnableGradientEffect() *bool + GetImageWebUrl() *string + GetLayout() *TitleAreaLayoutType + GetOdataType() *string + GetServerProcessedContent() ServerProcessedContentable + GetShowAuthor() *bool + GetShowPublishedDate() *bool + GetShowTextBlockAboveTitle() *bool + GetTextAboveTitle() *string + GetTextAlignment() *TitleAreaTextAlignmentType + SetAlternativeText(value *string) + SetEnableGradientEffect(value *bool) + SetImageWebUrl(value *string) + SetLayout(value *TitleAreaLayoutType) + SetOdataType(value *string) + SetServerProcessedContent(value ServerProcessedContentable) + SetShowAuthor(value *bool) + SetShowPublishedDate(value *bool) + SetShowTextBlockAboveTitle(value *bool) + SetTextAboveTitle(value *string) + SetTextAlignment(value *TitleAreaTextAlignmentType) +} diff --git a/src/internal/connector/graph/betasdk/models/vertical_section.go b/src/internal/connector/graph/betasdk/models/vertical_section.go new file mode 100644 index 000000000..e4e8f6ed5 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/vertical_section.go @@ -0,0 +1,104 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// VerticalSection +type VerticalSection struct { + msmodel.Entity + // Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. + emphasis *SectionEmphasisType + // The set of web parts in this section. + webparts []WebPartable +} + +// NewVerticalSection instantiates a new verticalSection and sets the default values. +func NewVerticalSection() *VerticalSection { + m := &VerticalSection{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateVerticalSectionFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateVerticalSectionFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewVerticalSection(), nil +} + +// GetEmphasis gets the emphasis property value. Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. +func (m *VerticalSection) GetEmphasis() *SectionEmphasisType { + return m.emphasis +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *VerticalSection) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + res["emphasis"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetEnumValue(ParseSectionEmphasisType) + if err != nil { + return err + } + if val != nil { + m.SetEmphasis(val.(*SectionEmphasisType)) + } + return nil + } + res["webparts"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]WebPartable, len(val)) + for i, v := range val { + res[i] = v.(WebPartable) + } + m.SetWebparts(res) + } + return nil + } + return res +} + +// GetWebparts gets the webparts property value. The set of web parts in this section. +func (m *VerticalSection) GetWebparts() []WebPartable { + return m.webparts +} + +// Serialize serializes information the current object +func (m *VerticalSection) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + if m.GetEmphasis() != nil { + cast := (*m.GetEmphasis()).String() + err = writer.WriteStringValue("emphasis", &cast) + if err != nil { + return err + } + } + if m.GetWebparts() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetWebparts())) + for i, v := range m.GetWebparts() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("webparts", cast) + if err != nil { + return err + } + } + return nil +} + +// SetEmphasis sets the emphasis property value. Enumeration value that indicates the emphasis of the section background. The possible values are: none, netural, soft, strong, unknownFutureValue. +func (m *VerticalSection) SetEmphasis(value *SectionEmphasisType) { + m.emphasis = value +} + +// SetWebparts sets the webparts property value. The set of web parts in this section. +func (m *VerticalSection) SetWebparts(value []WebPartable) { + m.webparts = value +} diff --git a/src/internal/connector/graph/betasdk/models/vertical_sectionable.go b/src/internal/connector/graph/betasdk/models/vertical_sectionable.go new file mode 100644 index 000000000..f4f0a991b --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/vertical_sectionable.go @@ -0,0 +1,16 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// VerticalSectionable +type VerticalSectionable interface { + msmodel.Entityable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetEmphasis() *SectionEmphasisType + GetWebparts() []WebPartable + SetEmphasis(value *SectionEmphasisType) + SetWebparts(value []WebPartable) +} diff --git a/src/internal/connector/graph/betasdk/models/web_part.go b/src/internal/connector/graph/betasdk/models/web_part.go new file mode 100644 index 000000000..3287051b1 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part.go @@ -0,0 +1,59 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPart provides operations to call the remove method. +type WebPart struct { + msmodel.Entity +} + +// NewWebPart instantiates a new webPart and sets the default values. +func NewWebPart() *WebPart { + m := &WebPart{ + Entity: *msmodel.NewEntity(), + } + return m +} + +// CreateWebPartFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + if parseNode != nil { + mappingValueNode, err := parseNode.GetChildNode("@odata.type") + if err != nil { + return nil, err + } + if mappingValueNode != nil { + mappingValue, err := mappingValueNode.GetStringValue() + if err != nil { + return nil, err + } + if mappingValue != nil { + switch *mappingValue { + case "#microsoft.graph.standardWebPart": + return NewStandardWebPart(), nil + case "#microsoft.graph.textWebPart": + return NewTextWebPart(), nil + } + } + } + } + return NewWebPart(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *WebPart) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.Entity.GetFieldDeserializers() + return res +} + +// Serialize serializes information the current object +func (m *WebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.Entity.Serialize(writer) + if err != nil { + return err + } + return nil +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_collection_response.go b/src/internal/connector/graph/betasdk/models/web_part_collection_response.go new file mode 100644 index 000000000..e590431b2 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_collection_response.go @@ -0,0 +1,75 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPartCollectionResponse provides operations to manage the webParts property of the microsoft.graph.sitePage entity. +type WebPartCollectionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []WebPartable +} + +// NewWebPartCollectionResponse instantiates a new WebPartCollectionResponse and sets the default values. +func NewWebPartCollectionResponse() *WebPartCollectionResponse { + m := &WebPartCollectionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateWebPartCollectionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateWebPartCollectionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewWebPartCollectionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *WebPartCollectionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(CreateWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]WebPartable, len(val)) + for i, v := range val { + res[i] = v.(WebPartable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +func (m *WebPartCollectionResponse) GetValue() []WebPartable { + return m.value +} + +// Serialize serializes information the current object +func (m *WebPartCollectionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +func (m *WebPartCollectionResponse) SetValue(value []WebPartable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_collection_responseable.go b/src/internal/connector/graph/betasdk/models/web_part_collection_responseable.go new file mode 100644 index 000000000..63aa0cae2 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_collection_responseable.go @@ -0,0 +1,14 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPartCollectionResponseable +type WebPartCollectionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []WebPartable + SetValue(value []WebPartable) +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_data.go b/src/internal/connector/graph/betasdk/models/web_part_data.go new file mode 100644 index 000000000..251ed2dc9 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_data.go @@ -0,0 +1,251 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPartData +type WebPartData struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Audience information of the web part. By using this property, specific content will be prioritized to specific audiences. + audiences []string + // Data version of the web part. The value is defined by the web part developer. Different dataVersions usually refers to a different property structure. + dataVersion *string + // Description of the web part. + description *string + // The OdataType property + odataType *string + // Properties bag of the web part. + properties msmodel.Jsonable + // Contains collections of data that can be processed by server side services like search index and link fixup. + serverProcessedContent ServerProcessedContentable + // Title of the web part. + title *string +} + +// NewWebPartData instantiates a new webPartData and sets the default values. +func NewWebPartData() *WebPartData { + m := &WebPartData{} + m.SetAdditionalData(make(map[string]interface{})) + return m +} + +// CreateWebPartDataFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateWebPartDataFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewWebPartData(), nil +} + +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *WebPartData) GetAdditionalData() map[string]interface{} { + return m.additionalData +} + +// GetAudiences gets the audiences property value. Audience information of the web part. By using this property, specific content will be prioritized to specific audiences. +func (m *WebPartData) GetAudiences() []string { + return m.audiences +} + +// GetDataVersion gets the dataVersion property value. Data version of the web part. The value is defined by the web part developer. Different dataVersions usually refers to a different property structure. +func (m *WebPartData) GetDataVersion() *string { + return m.dataVersion +} + +// GetDescription gets the description property value. Description of the web part. +func (m *WebPartData) GetDescription() *string { + return m.description +} + +// GetFieldDeserializers the deserialization information for the current model +func (m *WebPartData) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["audiences"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfPrimitiveValues("string") + if err != nil { + return err + } + if val != nil { + res := make([]string, len(val)) + for i, v := range val { + res[i] = *(v.(*string)) + } + m.SetAudiences(res) + } + return nil + } + res["dataVersion"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetDataVersion(val) + } + return nil + } + res["description"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetDescription(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["properties"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(msmodel.CreateJsonFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetProperties(val.(msmodel.Jsonable)) + } + return nil + } + res["serverProcessedContent"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateServerProcessedContentFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetServerProcessedContent(val.(ServerProcessedContentable)) + } + return nil + } + res["title"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetTitle(val) + } + return nil + } + return res +} + +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *WebPartData) GetOdataType() *string { + return m.odataType +} + +// GetProperties gets the properties property value. Properties bag of the web part. +func (m *WebPartData) GetProperties() msmodel.Jsonable { + return m.properties +} + +// GetServerProcessedContent gets the serverProcessedContent property value. Contains collections of data that can be processed by server side services like search index and link fixup. +func (m *WebPartData) GetServerProcessedContent() ServerProcessedContentable { + return m.serverProcessedContent +} + +// GetTitle gets the title property value. Title of the web part. +func (m *WebPartData) GetTitle() *string { + return m.title +} + +// Serialize serializes information the current object +func (m *WebPartData) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + if m.GetAudiences() != nil { + err := writer.WriteCollectionOfStringValues("audiences", m.GetAudiences()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("dataVersion", m.GetDataVersion()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("description", m.GetDescription()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteObjectValue("properties", m.GetProperties()) + if err != nil { + return err + } + } + { + err := writer.WriteObjectValue("serverProcessedContent", m.GetServerProcessedContent()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("title", m.GetTitle()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} + +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *WebPartData) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value +} + +// SetAudiences sets the audiences property value. Audience information of the web part. By using this property, specific content will be prioritized to specific audiences. +func (m *WebPartData) SetAudiences(value []string) { + m.audiences = value +} + +// SetDataVersion sets the dataVersion property value. Data version of the web part. The value is defined by the web part developer. Different dataVersions usually refers to a different property structure. +func (m *WebPartData) SetDataVersion(value *string) { + m.dataVersion = value +} + +// SetDescription sets the description property value. Description of the web part. +func (m *WebPartData) SetDescription(value *string) { + m.description = value +} + +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *WebPartData) SetOdataType(value *string) { + m.odataType = value +} + +// SetProperties sets the properties property value. Properties bag of the web part. +func (m *WebPartData) SetProperties(value msmodel.Jsonable) { + m.properties = value +} + +// SetServerProcessedContent sets the serverProcessedContent property value. Contains collections of data that can be processed by server side services like search index and link fixup. +func (m *WebPartData) SetServerProcessedContent(value ServerProcessedContentable) { + m.serverProcessedContent = value +} + +// SetTitle sets the title property value. Title of the web part. +func (m *WebPartData) SetTitle(value *string) { + m.title = value +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_dataable.go b/src/internal/connector/graph/betasdk/models/web_part_dataable.go new file mode 100644 index 000000000..a27a6d158 --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_dataable.go @@ -0,0 +1,26 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPartDataable +type WebPartDataable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetAudiences() []string + GetDataVersion() *string + GetDescription() *string + GetOdataType() *string + GetProperties() msmodel.Jsonable + GetServerProcessedContent() ServerProcessedContentable + GetTitle() *string + SetAudiences(value []string) + SetDataVersion(value *string) + SetDescription(value *string) + SetOdataType(value *string) + SetProperties(value msmodel.Jsonable) + SetServerProcessedContent(value ServerProcessedContentable) + SetTitle(value *string) +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_position.go b/src/internal/connector/graph/betasdk/models/web_part_position.go new file mode 100644 index 000000000..f2f1c3c9e --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_position.go @@ -0,0 +1,175 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// WebPartPosition +type WebPartPosition struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Indicates the identifier of the column where the web part is located. + columnId *float64 + // Indicates the horizontal section where the web part is located. + horizontalSectionId *float64 + // Indicates whether the web part is located in the vertical section. + isInVerticalSection *bool + // The OdataType property + odataType *string + // Index of the current web part. Represents the order of the web part in this column or section. + webPartIndex *float64 +} +// NewWebPartPosition instantiates a new webPartPosition and sets the default values. +func NewWebPartPosition()(*WebPartPosition) { + m := &WebPartPosition{ + } + m.SetAdditionalData(make(map[string]interface{})); + return m +} +// CreateWebPartPositionFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +func CreateWebPartPositionFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewWebPartPosition(), nil +} +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *WebPartPosition) GetAdditionalData()(map[string]interface{}) { + return m.additionalData +} +// GetColumnId gets the columnId property value. Indicates the identifier of the column where the web part is located. +func (m *WebPartPosition) GetColumnId()(*float64) { + return m.columnId +} +// GetFieldDeserializers the deserialization information for the current model +func (m *WebPartPosition) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) + res["columnId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetColumnId(val) + } + return nil + } + res["horizontalSectionId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetHorizontalSectionId(val) + } + return nil + } + res["isInVerticalSection"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetIsInVerticalSection(val) + } + return nil + } + res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["webPartIndex"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetWebPartIndex(val) + } + return nil + } + return res +} +// GetHorizontalSectionId gets the horizontalSectionId property value. Indicates the horizontal section where the web part is located. +func (m *WebPartPosition) GetHorizontalSectionId()(*float64) { + return m.horizontalSectionId +} +// GetIsInVerticalSection gets the isInVerticalSection property value. Indicates whether the web part is located in the vertical section. +func (m *WebPartPosition) GetIsInVerticalSection()(*bool) { + return m.isInVerticalSection +} +// GetOdataType gets the @odata.type property value. The OdataType property +func (m *WebPartPosition) GetOdataType()(*string) { + return m.odataType +} +// GetWebPartIndex gets the webPartIndex property value. Index of the current web part. Represents the order of the web part in this column or section. +func (m *WebPartPosition) GetWebPartIndex()(*float64) { + return m.webPartIndex +} +// Serialize serializes information the current object +func (m *WebPartPosition) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { + { + err := writer.WriteFloat64Value("columnId", m.GetColumnId()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("horizontalSectionId", m.GetHorizontalSectionId()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("isInVerticalSection", m.GetIsInVerticalSection()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("webPartIndex", m.GetWebPartIndex()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +func (m *WebPartPosition) SetAdditionalData(value map[string]interface{})() { + m.additionalData = value +} +// SetColumnId sets the columnId property value. Indicates the identifier of the column where the web part is located. +func (m *WebPartPosition) SetColumnId(value *float64)() { + m.columnId = value +} +// SetHorizontalSectionId sets the horizontalSectionId property value. Indicates the horizontal section where the web part is located. +func (m *WebPartPosition) SetHorizontalSectionId(value *float64)() { + m.horizontalSectionId = value +} +// SetIsInVerticalSection sets the isInVerticalSection property value. Indicates whether the web part is located in the vertical section. +func (m *WebPartPosition) SetIsInVerticalSection(value *bool)() { + m.isInVerticalSection = value +} +// SetOdataType sets the @odata.type property value. The OdataType property +func (m *WebPartPosition) SetOdataType(value *string)() { + m.odataType = value +} +// SetWebPartIndex sets the webPartIndex property value. Index of the current web part. Represents the order of the web part in this column or section. +func (m *WebPartPosition) SetWebPartIndex(value *float64)() { + m.webPartIndex = value +} diff --git a/src/internal/connector/graph/betasdk/models/web_part_positionable.go b/src/internal/connector/graph/betasdk/models/web_part_positionable.go new file mode 100644 index 000000000..f0939db2e --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_part_positionable.go @@ -0,0 +1,21 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" +) + +// WebPartPositionable +type WebPartPositionable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetColumnId()(*float64) + GetHorizontalSectionId()(*float64) + GetIsInVerticalSection()(*bool) + GetOdataType()(*string) + GetWebPartIndex()(*float64) + SetColumnId(value *float64)() + SetHorizontalSectionId(value *float64)() + SetIsInVerticalSection(value *bool)() + SetOdataType(value *string)() + SetWebPartIndex(value *float64)() +} diff --git a/src/internal/connector/graph/betasdk/models/web_partable.go b/src/internal/connector/graph/betasdk/models/web_partable.go new file mode 100644 index 000000000..ac8a2a84d --- /dev/null +++ b/src/internal/connector/graph/betasdk/models/web_partable.go @@ -0,0 +1,12 @@ +package models + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" +) + +// WebPartable +type WebPartable interface { + msmodel.Entityable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable +} diff --git a/src/internal/connector/graph/betasdk/sites/count_request_builder.go b/src/internal/connector/graph/betasdk/sites/count_request_builder.go new file mode 100644 index 000000000..f36f1c220 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/count_request_builder.go @@ -0,0 +1,116 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// CountRequestBuilder provides operations to count the resources in the collection. +type CountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// CountRequestBuilderGetQueryParameters get the number of the resource +type CountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// CountRequestBuilderGetRequestConfiguration configuration for the request such as headers, +// query parameters, and middleware options. +type CountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *CountRequestBuilderGetQueryParameters +} + +// NewCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +func NewCountRequestBuilderInternal( + pathParameters map[string]string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *CountRequestBuilder { + m := &CountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +func NewCountRequestBuilder( + rawURL string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *CountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawURL + + return NewCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +func (m *CountRequestBuilder) CreateGetRequestInformation( + ctx context.Context, + requestConfiguration *CountRequestBuilderGetRequestConfiguration, +) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:lll +func (m *CountRequestBuilder) Get(ctx context.Context, requestConfiguration *CountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_count_request_builder.go new file mode 100644 index 000000000..284366a30 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_count_request_builder.go @@ -0,0 +1,115 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesCountRequestBuilder provides operations to count the resources in the collection. +type ItemPagesCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesCountRequestBuilderGetQueryParameters get the number of the resource +type ItemPagesCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesCountRequestBuilderGetRequestConfiguration configuration for the request +// such as headers, query parameters, and middleware options. +type ItemPagesCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesCountRequestBuilderGetQueryParameters +} + +// NewItemPagesCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +func NewItemPagesCountRequestBuilderInternal( + pathParameters map[string]string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesCountRequestBuilder { + m := &ItemPagesCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +func NewItemPagesCountRequestBuilder( + rawURL string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawURL + + return NewItemPagesCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:lll +func (m *ItemPagesCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:lll +func (m *ItemPagesCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_count_request_builder.go new file mode 100644 index 000000000..deec0ca5f --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_count_request_builder.go @@ -0,0 +1,122 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder provides +// operations to count the resources in the collection. +type ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetQueryParameters get the number of the resource +type ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetRequestConfiguration configuration for +// the request such as headers, query parameters, and middleware options. +type ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetQueryParameters +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderInternal instantiates a +// new CountRequestBuilder and sets the default values. +func NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderInternal( + pathParameters map[string]string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder{} + //nolint:lll + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder instantiates a +// new CountRequestBuilder and sets the default values. +func NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder( + rawURL string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawURL + + return NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder) CreateGetRequestInformation( + ctx context.Context, + requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetRequestConfiguration, +) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error, +) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Get get the number of the resource +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder) Get( + ctx context.Context, + requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderGetRequestConfiguration, +) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_horizontal_section_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_horizontal_section_item_request_builder.go new file mode 100644 index 000000000..0845ecee4 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_horizontal_section_item_request_builder.go @@ -0,0 +1,260 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder +// provides operations to manage the horizontalSections property of the microsoft.graph.canvasLayout entity. +type ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderDeleteRequestConfiguration +// configuration for the request such as headers, query parameters, and middleware options. +type ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetQueryParameters +// collection of horizontal sections on the SharePoint page. +type ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetRequestConfiguration +// configuration for the request such as headers, query parameters, and middleware options. +type ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderPatchRequestConfiguration +// configuration for the request such as headers, query parameters, and middleware options. +type ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// Columns provides operations to manage the columns property of the microsoft.graph.horizontalSection entity. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) Columns() *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// ColumnsById provides operations to manage the columns property of the microsoft.graph.horizontalSection entity. +// +//nolint:revive +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) ColumnsById(id string, +) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + + if id != "" { + urlTplParams["horizontalSectionColumn%2Did"] = id + } + //nolint:lll + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderInternal +// instantiates a new HorizontalSectionItemRequestBuilder and sets the default values. +func NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderInternal( + pathParameters map[string]string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder{} + //nolint:lll + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder instantiates a +// new HorizontalSectionItemRequestBuilder and sets the default values. +func NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder( + rawURL string, + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter, +) *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawURL + + return NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderInternal( + urlParams, + requestAdapter, + ) +} + +// CreateDeleteRequestInformation delete navigation property horizontalSections for sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) CreateDeleteRequestInformation( + ctx context.Context, + requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderDeleteRequestConfiguration, +) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// CreateGetRequestInformation collection of horizontal sections on the SharePoint page. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) CreateGetRequestInformation( + ctx context.Context, + requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetRequestConfiguration, +) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property horizontalSections in sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + + err := requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if err != nil { + return nil, err + } + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Delete delete navigation property horizontalSections for sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + + if err != nil { + return err + } + + return nil +} + +// Get collection of horizontal sections on the SharePoint page. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable), nil +} + +// Patch update the navigation property horizontalSections in sites +// nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_count_request_builder.go new file mode 100644 index 000000000..95a2e7314 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_count_request_builder.go @@ -0,0 +1,117 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder +// provides operations to count the resources in the collection. +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetQueryParameters get the number of the resource +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetQueryParameters +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder(rawURL string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawURL + + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_horizontal_section_column_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_horizontal_section_column_item_request_builder.go new file mode 100644 index 000000000..c1a8315ce --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_horizontal_section_column_item_request_builder.go @@ -0,0 +1,247 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder provides operations to manage the columns property of the microsoft.graph.horizontalSection entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetQueryParameters the set of vertical columns in this section. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderInternal instantiates a new HorizontalSectionColumnItemRequestBuilder and sets the default values. +// +//nolint:lll +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/{horizontalSectionColumn%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder instantiates a new HorizontalSectionColumnItemRequestBuilder and sets the default values. +// +//nolint:lll, revive +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property columns for sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// CreateGetRequestInformation the set of vertical columns in this section. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property columns in sites +// +//nolint:lll,errcheck +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Delete delete navigation property columns for sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + + return nil +} + +// Get the set of vertical columns in this section. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionColumnFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable), nil +} + +// Patch update the navigation property columns in sites +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionColumnFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + + if res == nil { + return nil, nil + } + + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable), nil +} + +// Webparts provides operations to manage the webparts property of the microsoft.graph.horizontalSectionColumn entity. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) Webparts() *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// WebpartsById provides operations to manage the webparts property of the microsoft.graph.horizontalSectionColumn entity. +// +//nolint:lll,revive +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsHorizontalSectionColumnItemRequestBuilder) WebpartsById(id string) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + + if id != "" { + urlTplParams["webPart%2Did"] = id + } + + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_count_request_builder.go new file mode 100644 index 000000000..b5c6e3a40 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_count_request_builder.go @@ -0,0 +1,111 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder provides operations to count the resources in the collection. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetQueryParameters get the number of the resource +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetQueryParameters +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/{horizontalSectionColumn%2Did}/webparts/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll, revive +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_item_get_position_of_web_part_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_item_get_position_of_web_part_request_builder.go new file mode 100644 index 000000000..14429d80f --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_item_get_position_of_web_part_request_builder.go @@ -0,0 +1,96 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder provides operations to call the getPositionOfWebPart method. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderInternal instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/{horizontalSectionColumn%2Did}/webparts/{webPart%2Did}/microsoft.graph.getPositionOfWebPart" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreatePostRequestInformation invoke action getPositionOfWebPart +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder) CreatePostRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// nolint:lll,wsl +// Post invoke action getPositionOfWebPart +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/webpart-getposition?view=graph-rest-1.0 +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder) Post(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartPositionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_request_builder.go new file mode 100644 index 000000000..4d2a94186 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_request_builder.go @@ -0,0 +1,180 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder provides operations to manage the webparts property of the microsoft.graph.horizontalSectionColumn entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetQueryParameters get the webPart resources from a sitePage. Sort by the order in which they appear on the page. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderInternal instantiates a new WebpartsRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/{horizontalSectionColumn%2Did}/webparts{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder instantiates a new WebpartsRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder) Count() *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation get the webPart resources from a sitePage. Sort by the order in which they appear on the page. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create new navigation property to webparts for sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the webPart resources from a sitePage. Sort by the order in which they appear on the page. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/webpart-list?view=graph-rest-1.0 +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable), nil +} + +// Post create new navigation property to webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_web_part_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_web_part_item_request_builder.go new file mode 100644 index 000000000..0ce7becda --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_item_webparts_web_part_item_request_builder.go @@ -0,0 +1,209 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder provides operations to manage the webparts property of the microsoft.graph.horizontalSectionColumn entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetQueryParameters the collection of WebParts in this column. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderInternal instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns/{horizontalSectionColumn%2Did}/webparts/{webPart%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation the collection of WebParts in this column. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property webparts in sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get the collection of WebParts in this column. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} + +// GetPositionOfWebPart provides operations to call the getPositionOfWebPart method. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) GetPositionOfWebPart() *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsItemGetPositionOfWebPartRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// Patch update the navigation property webparts in sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsItemWebpartsWebPartItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_request_builder.go new file mode 100644 index 000000000..368738104 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_item_columns_request_builder.go @@ -0,0 +1,180 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder provides operations to manage the columns property of the microsoft.graph.horizontalSection entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetQueryParameters get a list of the horizontalSectionColumn objects and their properties. Sort by `id` in ascending order. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderInternal instantiates a new ColumnsRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections/{horizontalSection%2Did}/columns{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder instantiates a new ColumnsRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder) Count() *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation get a list of the horizontalSectionColumn objects and their properties. Sort by `id` in ascending order. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create new navigation property to columns for sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get a list of the horizontalSectionColumn objects and their properties. Sort by `id` in ascending order. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/horizontalsectioncolumn-list?view=graph-rest-1.0 +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionColumnCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnCollectionResponseable), nil +} + +// Post create new navigation property to columns for sites +// +//nolint:lll, wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsItemColumnsRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionColumnFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionColumnable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_request_builder.go new file mode 100644 index 000000000..829b830f0 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_horizontal_sections_request_builder.go @@ -0,0 +1,179 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder provides operations to manage the horizontalSections property of the microsoft.graph.canvasLayout entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetQueryParameters get a list of the horizontalSection objects and their properties. Sort by `id` in ascending order. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderInternal instantiates a new HorizontalSectionsRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder { + m := &ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/horizontalSections{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder instantiates a new HorizontalSectionsRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder) Count() *ItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation get a list of the horizontalSection objects and their properties. Sort by `id` in ascending order. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create new navigation property to horizontalSections for sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get a list of the horizontalSection objects and their properties. Sort by `id` in ascending order. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/horizontalsection-list?view=graph-rest-1.0 +// +//nolint:lll, wsl +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionCollectionResponseable), nil +} + +// nolint:lll,wsl +// Post create new navigation property to horizontalSections for sites +func (m *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateHorizontalSectionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.HorizontalSectionable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_request_builder.go new file mode 100644 index 000000000..bbca05a9b --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_request_builder.go @@ -0,0 +1,230 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutRequestBuilder provides operations to manage the canvasLayout property of the microsoft.graph.sitePage entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutRequestBuilderGetQueryParameters indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section +// +//nolint:lll +type ItemPagesItemCanvasLayoutRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutRequestBuilderInternal instantiates a new CanvasLayoutRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutRequestBuilder { + m := &ItemPagesItemCanvasLayoutRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutRequestBuilder instantiates a new CanvasLayoutRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewItemPagesItemCanvasLayoutRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property canvasLayout for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property canvasLayout in sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property canvasLayout for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateCanvasLayoutFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable), nil +} + +// HorizontalSections provides operations to manage the horizontalSections property of the microsoft.graph.canvasLayout entity. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutRequestBuilder) HorizontalSections() *ItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilder { + return NewItemPagesItemCanvasLayoutHorizontalSectionsRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// HorizontalSectionsById provides operations to manage the horizontalSections property of the microsoft.graph.canvasLayout entity. +// +//nolint:lll,wsl,revive +func (m *ItemPagesItemCanvasLayoutRequestBuilder) HorizontalSectionsById(id string) *ItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["horizontalSection%2Did"] = id + } + return NewItemPagesItemCanvasLayoutHorizontalSectionsHorizontalSectionItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} + +// Patch update the navigation property canvasLayout in sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable, requestConfiguration *ItemPagesItemCanvasLayoutRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateCanvasLayoutFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CanvasLayoutable), nil +} + +// VerticalSection provides operations to manage the verticalSection property of the microsoft.graph.canvasLayout entity. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutRequestBuilder) VerticalSection() *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder { + return NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_request_builder.go new file mode 100644 index 000000000..3fbb916ca --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_request_builder.go @@ -0,0 +1,226 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder provides operations to manage the verticalSection property of the microsoft.graph.canvasLayout entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetQueryParameters read the properties and relationships of a verticalSection object. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilderInternal instantiates a new VerticalSectionRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder { + m := &ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/verticalSection{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilder instantiates a new VerticalSectionRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutVerticalSectionRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property verticalSection for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation read the properties and relationships of a verticalSection object. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property verticalSection in sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property verticalSection for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get read the properties and relationships of a verticalSection object. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/verticalsection-get?view=graph-rest-1.0 +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateVerticalSectionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable), nil +} + +// Patch update the navigation property verticalSection in sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateVerticalSectionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.VerticalSectionable), nil +} + +// Webparts provides operations to manage the webparts property of the microsoft.graph.verticalSection entity. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) Webparts() *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder { + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// WebpartsById provides operations to manage the webparts property of the microsoft.graph.verticalSection entity. +// +//nolint:lll,wsl,revive +func (m *ItemPagesItemCanvasLayoutVerticalSectionRequestBuilder) WebpartsById(id string) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["webPart%2Did"] = id + } + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_count_request_builder.go new file mode 100644 index 000000000..774c55f28 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_count_request_builder.go @@ -0,0 +1,107 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder provides operations to count the resources in the collection. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetQueryParameters get the number of the resource +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetQueryParameters +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder { + m := &ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/verticalSection/webparts/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_item_get_position_of_web_part_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_item_get_position_of_web_part_request_builder.go new file mode 100644 index 000000000..d63ff729a --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_item_get_position_of_web_part_request_builder.go @@ -0,0 +1,97 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder provides operations to call the getPositionOfWebPart method. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderInternal instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder { + m := &ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/verticalSection/webparts/{webPart%2Did}/microsoft.graph.getPositionOfWebPart" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreatePostRequestInformation invoke action getPositionOfWebPart +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder) CreatePostRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Post invoke action getPositionOfWebPart +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/webpart-getposition?view=graph-rest-1.0 +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder) Post(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartPositionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_request_builder.go new file mode 100644 index 000000000..bd1885be1 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_request_builder.go @@ -0,0 +1,177 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder provides operations to manage the webparts property of the microsoft.graph.verticalSection entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetQueryParameters the set of web parts in this section. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderInternal instantiates a new WebpartsRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder { + m := &ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/verticalSection/webparts{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder instantiates a new WebpartsRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder) Count() *ItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilder { + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation the set of web parts in this section. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create new navigation property to webparts for sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get the set of web parts in this section. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable), nil +} + +// Post create new navigation property to webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_web_part_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_web_part_item_request_builder.go new file mode 100644 index 000000000..fdb5025c5 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_canvas_layout_vertical_section_webparts_web_part_item_request_builder.go @@ -0,0 +1,209 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder provides operations to manage the webparts property of the microsoft.graph.verticalSection entity. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetQueryParameters the set of web parts in this section. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetQueryParameters +} + +// ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderInternal instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder { + m := &ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/canvasLayout/verticalSection/webparts/{webPart%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation the set of web parts in this section. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property webparts in sites +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property webparts for sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get the set of web parts in this section. +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} + +// GetPositionOfWebPart provides operations to call the getPositionOfWebPart method. +// +//nolint:lll +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) GetPositionOfWebPart() *ItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilder { + return NewItemPagesItemCanvasLayoutVerticalSectionWebpartsItemGetPositionOfWebPartRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// Patch update the navigation property webparts in sites +// +//nolint:lll,wsl +func (m *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemCanvasLayoutVerticalSectionWebpartsWebPartItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_body.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_body.go new file mode 100644 index 000000000..5150584d8 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_body.go @@ -0,0 +1,182 @@ +package sites + +import i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + +// ItemPagesItemGetWebPartsByPositionPostRequestBody provides operations to call the getWebPartsByPosition method. +// +//nolint:lll,revive +type ItemPagesItemGetWebPartsByPositionPostRequestBody struct { + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // The columnId property + columnId *float64 + // The horizontalSectionId property + horizontalSectionId *float64 + // The isInVerticalSection property + isInVerticalSection *bool + // The webPartIndex property + webPartIndex *float64 +} + +// NewItemPagesItemGetWebPartsByPositionPostRequestBody instantiates a new ItemPagesItemGetWebPartsByPositionPostRequestBody and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemGetWebPartsByPositionPostRequestBody() *ItemPagesItemGetWebPartsByPositionPostRequestBody { + m := &ItemPagesItemGetWebPartsByPositionPostRequestBody{} + m.SetAdditionalData(make(map[string]interface{})) + return m +} + +// CreateItemPagesItemGetWebPartsByPositionPostRequestBodyFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +// +//nolint:lll +func CreateItemPagesItemGetWebPartsByPositionPostRequestBodyFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewItemPagesItemGetWebPartsByPositionPostRequestBody(), nil +} + +// GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +// +//nolint:lll +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetAdditionalData() map[string]interface{} { + return m.additionalData +} + +// GetColumnId gets the columnId property value. The columnId property +// +//nolint:lll,revive +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetColumnId() *float64 { + return m.columnId +} + +// GetFieldDeserializers the deserialization information for the current model +// +//nolint:lll,wsl,revive +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["columnId"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetColumnId(val) + } + return nil + } + res["horizontalSectionId"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetHorizontalSectionId(val) + } + return nil + } + res["isInVerticalSection"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetIsInVerticalSection(val) + } + return nil + } + res["webPartIndex"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetWebPartIndex(val) + } + return nil + } + return res +} + +// GetHorizontalSectionId gets the horizontalSectionId property value. The horizontalSectionId property +// +//nolint:lll,revive +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetHorizontalSectionId() *float64 { + return m.horizontalSectionId +} + +// GetIsInVerticalSection gets the isInVerticalSection property value. The isInVerticalSection property +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetIsInVerticalSection() *bool { + return m.isInVerticalSection +} + +// GetWebPartIndex gets the webPartIndex property value. The webPartIndex property +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) GetWebPartIndex() *float64 { + return m.webPartIndex +} + +// Serialize serializes information the current object +// +//nolint:lll,wsl +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteFloat64Value("columnId", m.GetColumnId()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("horizontalSectionId", m.GetHorizontalSectionId()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("isInVerticalSection", m.GetIsInVerticalSection()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("webPartIndex", m.GetWebPartIndex()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil +} + +// SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. +// +//nolint:lll +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value +} + +// SetColumnId sets the columnId property value. The columnId property +// +//nolint:lll,revive +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) SetColumnId(value *float64) { + m.columnId = value +} + +// SetHorizontalSectionId sets the horizontalSectionId property value. The horizontalSectionId property +// +//nolint:revive +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) SetHorizontalSectionId(value *float64) { + m.horizontalSectionId = value +} + +// SetIsInVerticalSection sets the isInVerticalSection property value. The isInVerticalSection property +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) SetIsInVerticalSection(value *bool) { + m.isInVerticalSection = value +} + +// SetWebPartIndex sets the webPartIndex property value. The webPartIndex property +func (m *ItemPagesItemGetWebPartsByPositionPostRequestBody) SetWebPartIndex(value *float64) { + m.webPartIndex = value +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_bodyable.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_bodyable.go new file mode 100644 index 000000000..9cad4e760 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_post_request_bodyable.go @@ -0,0 +1,17 @@ +package sites + +import i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + +// ItemPagesItemGetWebPartsByPositionPostRequestBodyable +type ItemPagesItemGetWebPartsByPositionPostRequestBodyable interface { + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetColumnId() *float64 + GetHorizontalSectionId() *float64 + GetIsInVerticalSection() *bool + GetWebPartIndex() *float64 + SetColumnId(value *float64) + SetHorizontalSectionId(value *float64) + SetIsInVerticalSection(value *bool) + SetWebPartIndex(value *float64) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_request_builder.go new file mode 100644 index 000000000..29ff2090b --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_request_builder.go @@ -0,0 +1,91 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemGetWebPartsByPositionRequestBuilder provides operations to call the getWebPartsByPosition method. +type ItemPagesItemGetWebPartsByPositionRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemGetWebPartsByPositionRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemGetWebPartsByPositionRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemGetWebPartsByPositionRequestBuilderInternal instantiates a new GetWebPartsByPositionRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemGetWebPartsByPositionRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemGetWebPartsByPositionRequestBuilder { + m := &ItemPagesItemGetWebPartsByPositionRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/microsoft.graph.getWebPartsByPosition" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemGetWebPartsByPositionRequestBuilder instantiates a new GetWebPartsByPositionRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemGetWebPartsByPositionRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemGetWebPartsByPositionRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemGetWebPartsByPositionRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreatePostRequestInformation invoke action getWebPartsByPosition +// +//nolint:lll,wsl,errcheck +func (m *ItemPagesItemGetWebPartsByPositionRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ItemPagesItemGetWebPartsByPositionPostRequestBodyable, requestConfiguration *ItemPagesItemGetWebPartsByPositionRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Post invoke action getWebPartsByPosition +// +//nolint:lll,wsl +func (m *ItemPagesItemGetWebPartsByPositionRequestBuilder) Post(ctx context.Context, body ItemPagesItemGetWebPartsByPositionPostRequestBodyable, requestConfiguration *ItemPagesItemGetWebPartsByPositionRequestBuilderPostRequestConfiguration) (ItemPagesItemGetWebPartsByPositionResponseable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, CreateItemPagesItemGetWebPartsByPositionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ItemPagesItemGetWebPartsByPositionResponseable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_response.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_response.go new file mode 100644 index 000000000..fa89d8855 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_response.go @@ -0,0 +1,89 @@ +package sites + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemGetWebPartsByPositionResponse provides operations to call the getWebPartsByPosition method. +type ItemPagesItemGetWebPartsByPositionResponse struct { + msmodel.BaseCollectionPaginationCountResponse + // The value property + value []ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable +} + +// NewItemPagesItemGetWebPartsByPositionResponse instantiates a new ItemPagesItemGetWebPartsByPositionResponse and sets the default values. +// +//nolint:wsl,lll +func NewItemPagesItemGetWebPartsByPositionResponse() *ItemPagesItemGetWebPartsByPositionResponse { + m := &ItemPagesItemGetWebPartsByPositionResponse{ + BaseCollectionPaginationCountResponse: *msmodel.NewBaseCollectionPaginationCountResponse(), + } + return m +} + +// CreateItemPagesItemGetWebPartsByPositionResponseFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value +// +//nolint:lll +func CreateItemPagesItemGetWebPartsByPositionResponseFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewItemPagesItemGetWebPartsByPositionResponse(), nil +} + +// GetFieldDeserializers the deserialization information for the current model +// +//nolint:lll,wsl +func (m *ItemPagesItemGetWebPartsByPositionResponse) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.BaseCollectionPaginationCountResponse.GetFieldDeserializers() + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetCollectionOfObjectValues(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + res := make([]ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, len(val)) + for i, v := range val { + res[i] = v.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable) + } + m.SetValue(res) + } + return nil + } + return res +} + +// GetValue gets the value property value. The value property +// +//nolint:lll +func (m *ItemPagesItemGetWebPartsByPositionResponse) GetValue() []ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable { + return m.value +} + +// Serialize serializes information the current object +// +//nolint:lll,wsl +func (m *ItemPagesItemGetWebPartsByPositionResponse) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.BaseCollectionPaginationCountResponse.Serialize(writer) + if err != nil { + return err + } + if m.GetValue() != nil { + cast := make([]i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, len(m.GetValue())) + for i, v := range m.GetValue() { + cast[i] = v.(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable) + } + err = writer.WriteCollectionOfObjectValues("value", cast) + if err != nil { + return err + } + } + return nil +} + +// SetValue sets the value property value. The value property +// +//nolint:lll +func (m *ItemPagesItemGetWebPartsByPositionResponse) SetValue(value []ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable) { + m.value = value +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_responseable.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_responseable.go new file mode 100644 index 000000000..f862929ab --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_get_web_parts_by_position_responseable.go @@ -0,0 +1,16 @@ +package sites + +import ( + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesItemGetWebPartsByPositionResponseable +type ItemPagesItemGetWebPartsByPositionResponseable interface { + msmodel.BaseCollectionPaginationCountResponseable + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetValue() []ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable + SetValue(value []ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_publish_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_publish_request_builder.go new file mode 100644 index 000000000..56197e940 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_publish_request_builder.go @@ -0,0 +1,82 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemPublishRequestBuilder provides operations to call the publish method. +type ItemPagesItemPublishRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// nolint:lll +// ItemPagesItemPublishRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +type ItemPagesItemPublishRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// nolint:lll,wsl +// NewItemPagesItemPublishRequestBuilderInternal instantiates a new PublishRequestBuilder and sets the default values. +func NewItemPagesItemPublishRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemPublishRequestBuilder { + m := &ItemPagesItemPublishRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/microsoft.graph.publish" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemPublishRequestBuilder instantiates a new PublishRequestBuilder and sets the default values. +// +//nolint:lll,wsl,revive +func NewItemPagesItemPublishRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemPublishRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemPublishRequestBuilderInternal(urlParams, requestAdapter) +} + +// //nolint:wsl,revive,lll +// CreatePostRequestInformation invoke action publish +func (m *ItemPagesItemPublishRequestBuilder) CreatePostRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemPublishRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// //nolint:wsl,revive,lll +// Post invoke action publish +func (m *ItemPagesItemPublishRequestBuilder) Post(ctx context.Context, requestConfiguration *ItemPagesItemPublishRequestBuilderPostRequestConfiguration) error { + requestInfo, err := m.CreatePostRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_count_request_builder.go new file mode 100644 index 000000000..57c875179 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_count_request_builder.go @@ -0,0 +1,103 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemWebPartsCountRequestBuilder provides operations to count the resources in the collection. +type ItemPagesItemWebPartsCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemWebPartsCountRequestBuilderGetQueryParameters get the number of the resource +type ItemPagesItemWebPartsCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemPagesItemWebPartsCountRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemWebPartsCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemWebPartsCountRequestBuilderGetQueryParameters +} + +// NewItemPagesItemWebPartsCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsCountRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsCountRequestBuilder { + m := &ItemPagesItemWebPartsCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/webParts/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemWebPartsCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsCountRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemWebPartsCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go new file mode 100644 index 000000000..4bb325673 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go @@ -0,0 +1,96 @@ +package sites + +import ( + "context" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder provides operations to call the getPositionOfWebPart method. +// +//nolint:lll +type ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderInternal instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder { + m := &ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/webParts/{webPart%2Did}/microsoft.graph.getPositionOfWebPart" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder instantiates a new GetPositionOfWebPartRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreatePostRequestInformation invoke action getPositionOfWebPart +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder) CreatePostRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Post invoke action getPositionOfWebPart +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/webpart-getposition?view=graph-rest-1.0 +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder) Post(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartPositionFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartPositionable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go new file mode 100644 index 000000000..0e349df74 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go @@ -0,0 +1,172 @@ +package sites + +import ( + "context" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemWebPartsRequestBuilder provides operations to manage the webParts property of the microsoft.graph.sitePage entity. +// +//nolint:lll +type ItemPagesItemWebPartsRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemWebPartsRequestBuilderGetQueryParameters collection of webparts on the SharePoint page +type ItemPagesItemWebPartsRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesItemWebPartsRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemWebPartsRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemWebPartsRequestBuilderGetQueryParameters +} + +// ItemPagesItemWebPartsRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesItemWebPartsRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemWebPartsRequestBuilderInternal instantiates a new WebPartsRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewItemPagesItemWebPartsRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsRequestBuilder { + m := &ItemPagesItemWebPartsRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/webParts{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemWebPartsRequestBuilder instantiates a new WebPartsRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemWebPartsRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +func (m *ItemPagesItemWebPartsRequestBuilder) Count() *ItemPagesItemWebPartsCountRequestBuilder { + return NewItemPagesItemWebPartsCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation collection of webparts on the SharePoint page +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create new navigation property to webParts for sites +// +//nolint:wsl,revive,lll,errcheck +func (m *ItemPagesItemWebPartsRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemWebPartsRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get collection of webparts on the SharePoint page +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartCollectionResponseable), nil +} + +// Post create new navigation property to webParts for sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemWebPartsRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go new file mode 100644 index 000000000..25dba98cf --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go @@ -0,0 +1,209 @@ +package sites + +import ( + "context" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesItemWebPartsWebPartItemRequestBuilder provides operations to manage the webParts property of the microsoft.graph.sitePage entity. +// +//nolint:wsl,revive,lll +type ItemPagesItemWebPartsWebPartItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesItemWebPartsWebPartItemRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesItemWebPartsWebPartItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesItemWebPartsWebPartItemRequestBuilderGetQueryParameters collection of webparts on the SharePoint page +// +//nolint:wsl,revive,lll +type ItemPagesItemWebPartsWebPartItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesItemWebPartsWebPartItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesItemWebPartsWebPartItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesItemWebPartsWebPartItemRequestBuilderGetQueryParameters +} + +// ItemPagesItemWebPartsWebPartItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesItemWebPartsWebPartItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesItemWebPartsWebPartItemRequestBuilderInternal instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsWebPartItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsWebPartItemRequestBuilder { + m := &ItemPagesItemWebPartsWebPartItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}/webParts/{webPart%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesItemWebPartsWebPartItemRequestBuilder instantiates a new WebPartItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesItemWebPartsWebPartItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesItemWebPartsWebPartItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesItemWebPartsWebPartItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property webParts for sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation collection of webparts on the SharePoint page +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property webParts in sites +// +//nolint:wsl,revive,lll,errcheck +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property webParts for sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get collection of webparts on the SharePoint page +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} + +// GetPositionOfWebPart provides operations to call the getPositionOfWebPart method. +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) GetPositionOfWebPart() *ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder { + return NewItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// Patch update the navigation property webParts in sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesItemWebPartsWebPartItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, requestConfiguration *ItemPagesItemWebPartsWebPartItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateWebPartFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.WebPartable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go new file mode 100644 index 000000000..43f503439 --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go @@ -0,0 +1,176 @@ +package sites + +import ( + "context" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemPagesRequestBuilder provides operations to manage the pages property of the microsoft.graph.site entity. +type ItemPagesRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesRequestBuilderGetQueryParameters get the collection of [sitePage][] objects from the site pages [list][] in a site [site][]. All pages in the site are returned (with pagination). Sort alphabetically by `name` in ascending order. +// +//nolint:lll +type ItemPagesRequestBuilderGetQueryParameters struct { + // Include count of items + Count *bool `uriparametername:"%24count"` + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Order items by property values + Orderby []string `uriparametername:"%24orderby"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` + // Skip the first n items + Skip *int32 `uriparametername:"%24skip"` + // Show only the first n items + Top *int32 `uriparametername:"%24top"` +} + +// ItemPagesRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesRequestBuilderGetQueryParameters +} + +// ItemPagesRequestBuilderPostRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesRequestBuilderPostRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// NewItemPagesRequestBuilderInternal instantiates a new PagesRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesRequestBuilder { + m := &ItemPagesRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages{?%24top,%24skip,%24search,%24filter,%24count,%24orderby,%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesRequestBuilder instantiates a new PagesRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesRequestBuilderInternal(urlParams, requestAdapter) +} + +// Count provides operations to count the resources in the collection. +func (m *ItemPagesRequestBuilder) Count() *ItemPagesCountRequestBuilder { + return NewItemPagesCountRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// CreateGetRequestInformation get the collection of [sitePage][] objects from the site pages [list][] in a site [site][]. All pages in the site are returned (with pagination). Sort alphabetically by `name` in ascending order. +// +//nolint:wsl,revive,lll +func (m *ItemPagesRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePostRequestInformation create a new [sitePage][] in the site pages [list][] in a [site][]. +// +//nolint:errcheck,lll,wsl +func (m *ItemPagesRequestBuilder) CreatePostRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, requestConfiguration *ItemPagesRequestBuilderPostRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.POST + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the collection of [sitePage][] objects from the site pages [list][] in a site [site][]. All pages in the site are returned (with pagination). Sort alphabetically by `name` in ascending order. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/sitepage-list?view=graph-rest-1.0 +// //nolint:wsl,revive,lll +func (m *ItemPagesRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageCollectionResponseable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateSitePageCollectionResponseFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageCollectionResponseable), nil +} + +// Post create a new [sitePage][] in the site pages [list][] in a [site][]. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/sitepage-create?view=graph-rest-1.0 +// nolint:wsl,revive,lll +func (m *ItemPagesRequestBuilder) Post(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, requestConfiguration *ItemPagesRequestBuilderPostRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, error) { + requestInfo, err := m.CreatePostRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateSitePageFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_site_page_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_site_page_item_request_builder.go new file mode 100644 index 000000000..29fda72bd --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_pages_site_page_item_request_builder.go @@ -0,0 +1,237 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" +) + +// ItemPagesSitePageItemRequestBuilder provides operations to manage the pages property of the microsoft.graph.site entity. +// +//nolint:lll +type ItemPagesSitePageItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemPagesSitePageItemRequestBuilderDeleteRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesSitePageItemRequestBuilderDeleteRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// ItemPagesSitePageItemRequestBuilderGetQueryParameters the collection of pages in the SitePages list in this site. +type ItemPagesSitePageItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemPagesSitePageItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemPagesSitePageItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemPagesSitePageItemRequestBuilderGetQueryParameters +} + +// ItemPagesSitePageItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:wsl,revive,lll +type ItemPagesSitePageItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// CanvasLayout provides operations to manage the canvasLayout property of the microsoft.graph.sitePage entity. +func (m *ItemPagesSitePageItemRequestBuilder) CanvasLayout() *ItemPagesItemCanvasLayoutRequestBuilder { + return NewItemPagesItemCanvasLayoutRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// NewItemPagesSitePageItemRequestBuilderInternal instantiates a new SitePageItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesSitePageItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesSitePageItemRequestBuilder { + m := &ItemPagesSitePageItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/pages/{sitePage%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemPagesSitePageItemRequestBuilder instantiates a new SitePageItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemPagesSitePageItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemPagesSitePageItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemPagesSitePageItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateDeleteRequestInformation delete navigation property pages for sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) CreateDeleteRequestInformation(ctx context.Context, requestConfiguration *ItemPagesSitePageItemRequestBuilderDeleteRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.DELETE + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreateGetRequestInformation the collection of pages in the SitePages list in this site. +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemPagesSitePageItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update the navigation property pages in sites +// +//nolint:errcheck,lll,wsl +func (m *ItemPagesSitePageItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, requestConfiguration *ItemPagesSitePageItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Delete delete navigation property pages for sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) Delete(ctx context.Context, requestConfiguration *ItemPagesSitePageItemRequestBuilderDeleteRequestConfiguration) error { + requestInfo, err := m.CreateDeleteRequestInformation(ctx, requestConfiguration) + if err != nil { + return err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + err = m.requestAdapter.SendNoContent(ctx, requestInfo, errorMapping) + if err != nil { + return err + } + return nil +} + +// Get the collection of pages in the SitePages list in this site. +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemPagesSitePageItemRequestBuilderGetRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateSitePageFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable), nil +} + +// GetWebPartsByPosition provides operations to call the getWebPartsByPosition method. +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) GetWebPartsByPosition() *ItemPagesItemGetWebPartsByPositionRequestBuilder { + return NewItemPagesItemGetWebPartsByPositionRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// Patch update the navigation property pages in sites +// +//nolint:wsl,revive,lll +func (m *ItemPagesSitePageItemRequestBuilder) Patch(ctx context.Context, body ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, requestConfiguration *ItemPagesSitePageItemRequestBuilderPatchRequestConfiguration) (ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.CreateSitePageFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354.SitePageable), nil +} + +// Publish provides operations to call the publish method. +func (m *ItemPagesSitePageItemRequestBuilder) Publish() *ItemPagesItemPublishRequestBuilder { + return NewItemPagesItemPublishRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// WebParts provides operations to manage the webParts property of the microsoft.graph.sitePage entity. +func (m *ItemPagesSitePageItemRequestBuilder) WebParts() *ItemPagesItemWebPartsRequestBuilder { + return NewItemPagesItemWebPartsRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// WebPartsById provides operations to manage the webParts property of the microsoft.graph.sitePage entity. +// +//nolint:revive,wsl +func (m *ItemPagesSitePageItemRequestBuilder) WebPartsById(id string) *ItemPagesItemWebPartsWebPartItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["webPart%2Did"] = id + } + return NewItemPagesItemWebPartsWebPartItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} diff --git a/src/internal/connector/graph/betasdk/sites/item_sites_count_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_sites_count_request_builder.go new file mode 100644 index 000000000..5a83c959c --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_sites_count_request_builder.go @@ -0,0 +1,103 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemSitesCountRequestBuilder provides operations to count the resources in the collection. +type ItemSitesCountRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemSitesCountRequestBuilderGetQueryParameters get the number of the resource +type ItemSitesCountRequestBuilderGetQueryParameters struct { + // Filter items by property values + Filter *string `uriparametername:"%24filter"` + // Search items by search phrases + Search *string `uriparametername:"%24search"` +} + +// ItemSitesCountRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemSitesCountRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemSitesCountRequestBuilderGetQueryParameters +} + +// NewItemSitesCountRequestBuilderInternal instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:wsl,lll +func NewItemSitesCountRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemSitesCountRequestBuilder { + m := &ItemSitesCountRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/sites/$count{?%24search,%24filter}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemSitesCountRequestBuilder instantiates a new CountRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemSitesCountRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemSitesCountRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemSitesCountRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation get the number of the resource +// +//nolint:wsl,revive,lll +func (m *ItemSitesCountRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemSitesCountRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "text/plain") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get get the number of the resource +// +//nolint:wsl,revive,lll +func (m *ItemSitesCountRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemSitesCountRequestBuilderGetRequestConfiguration) (*int32, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.SendPrimitive(ctx, requestInfo, "int32", errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(*int32), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/item_sites_site_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_sites_site_item_request_builder.go new file mode 100644 index 000000000..c5959b0ff --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/item_sites_site_item_request_builder.go @@ -0,0 +1,104 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// ItemSitesSiteItemRequestBuilder provides operations to manage the sites property of the microsoft.graph.site entity. +type ItemSitesSiteItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// ItemSitesSiteItemRequestBuilderGetQueryParameters the collection of the sub-sites under this site. +type ItemSitesSiteItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// ItemSitesSiteItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type ItemSitesSiteItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *ItemSitesSiteItemRequestBuilderGetQueryParameters +} + +// NewItemSitesSiteItemRequestBuilderInternal instantiates a new SiteItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemSitesSiteItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemSitesSiteItemRequestBuilder { + m := &ItemSitesSiteItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}/sites/{site%2Did1}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewItemSitesSiteItemRequestBuilder instantiates a new SiteItemRequestBuilder and sets the default values. +// +//nolint:wsl,revive,lll +func NewItemSitesSiteItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *ItemSitesSiteItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewItemSitesSiteItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// CreateGetRequestInformation the collection of the sub-sites under this site. +// +//nolint:wsl,revive,lll +func (m *ItemSitesSiteItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *ItemSitesSiteItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// Get the collection of the sub-sites under this site. +// +//nolint:wsl,revive,lll +func (m *ItemSitesSiteItemRequestBuilder) Get(ctx context.Context, requestConfiguration *ItemSitesSiteItemRequestBuilderGetRequestConfiguration) (msmodel.Siteable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, msmodel.CreateSiteFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(msmodel.Siteable), nil +} diff --git a/src/internal/connector/graph/betasdk/sites/site_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/site_item_request_builder.go new file mode 100644 index 000000000..3746a736c --- /dev/null +++ b/src/internal/connector/graph/betasdk/sites/site_item_request_builder.go @@ -0,0 +1,249 @@ +package sites + +import ( + "context" + + i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" + msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" + i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" +) + +// SiteItemRequestBuilder provides operations to manage the collection of site entities. +type SiteItemRequestBuilder struct { + // Path parameters for the request + pathParameters map[string]string + // The request adapter to use to execute the requests. + requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter + // Url template to use to build the URL for the current request builder + urlTemplate string +} + +// SiteItemRequestBuilderGetQueryParameters retrieve properties and relationships for a [site][] resource.A **site** resource represents a team site in SharePoint. +// +//nolint:lll +type SiteItemRequestBuilderGetQueryParameters struct { + // Expand related entities + Expand []string `uriparametername:"%24expand"` + // Select properties to be returned + Select []string `uriparametername:"%24select"` +} + +// SiteItemRequestBuilderGetRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type SiteItemRequestBuilderGetRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption + // Request query parameters + QueryParameters *SiteItemRequestBuilderGetQueryParameters +} + +// SiteItemRequestBuilderPatchRequestConfiguration configuration for the request such as headers, query parameters, and middleware options. +// +//nolint:lll +type SiteItemRequestBuilderPatchRequestConfiguration struct { + // Request headers + Headers *i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestHeaders + // Request options + Options []i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestOption +} + +// Analytics provides operations to manage the analytics property of the microsoft.graph.site entity. +// REMOVED Analytics for minimial + +// Columns provides operations to manage the columns property of the microsoft.graph.site entity. + +// ColumnsById provides operations to manage the columns property of the microsoft.graph.site entity. + +// NewSiteItemRequestBuilderInternal instantiates a new SiteItemRequestBuilder and sets the default values. +// +//nolint:lll,wsl +func NewSiteItemRequestBuilderInternal(pathParameters map[string]string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *SiteItemRequestBuilder { + m := &SiteItemRequestBuilder{} + m.urlTemplate = "{+baseurl}/sites/{site%2Did}{?%24select,%24expand}" + urlTplParams := make(map[string]string) + for idx, item := range pathParameters { + urlTplParams[idx] = item + } + m.pathParameters = urlTplParams + m.requestAdapter = requestAdapter + return m +} + +// NewSiteItemRequestBuilder instantiates a new SiteItemRequestBuilder and sets the default values. +// +//nolint:lll,revive,wsl +func NewSiteItemRequestBuilder(rawUrl string, requestAdapter i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestAdapter) *SiteItemRequestBuilder { + urlParams := make(map[string]string) + urlParams["request-raw-url"] = rawUrl + return NewSiteItemRequestBuilderInternal(urlParams, requestAdapter) +} + +// ContentTypes provides operations to manage the contentTypes property of the microsoft.graph.site entity. + +// ContentTypesById provides operations to manage the contentTypes property of the microsoft.graph.site entity. +// CreateGetRequestInformation retrieve properties and relationships for a [site][] resource.A **site** resource represents a team site in SharePoint. +// +//nolint:lll,wsl +func (m *SiteItemRequestBuilder) CreateGetRequestInformation(ctx context.Context, requestConfiguration *SiteItemRequestBuilderGetRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.GET + requestInfo.Headers.Add("Accept", "application/json") + if requestConfiguration != nil { + if requestConfiguration.QueryParameters != nil { + requestInfo.AddQueryParameters(*(requestConfiguration.QueryParameters)) + } + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + return requestInfo, nil +} + +// CreatePatchRequestInformation update entity in sites by key (id) +// +//nolint:lll,errcheck,wsl +func (m *SiteItemRequestBuilder) CreatePatchRequestInformation(ctx context.Context, body msmodel.Siteable, requestConfiguration *SiteItemRequestBuilderPatchRequestConfiguration) (*i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.RequestInformation, error) { + requestInfo := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.NewRequestInformation() + requestInfo.UrlTemplate = m.urlTemplate + requestInfo.PathParameters = m.pathParameters + requestInfo.Method = i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.PATCH + requestInfo.Headers.Add("Accept", "application/json") + requestInfo.SetContentFromParsable(ctx, m.requestAdapter, "application/json", body) + + if requestConfiguration != nil { + requestInfo.Headers.AddAll(requestConfiguration.Headers) + requestInfo.AddRequestOptions(requestConfiguration.Options) + } + + return requestInfo, nil +} + +// Drive provides operations to manage the drive property of the microsoft.graph.site entity. +// Removed Drive() for minimial connector + +// Drives provides operations to manage the drives property of the microsoft.graph.site entity. +// Removed Drives() + +// DrivesById provides operations to manage the drives property of the microsoft.graph.site entity. + +// ExternalColumns provides operations to manage the externalColumns property of the microsoft.graph.site entity. + +// // ExternalColumnsById provides operations to manage the externalColumns property of the microsoft.graph.site entity. +// +// func (m *SiteItemRequestBuilder) ExternalColumnsById(id string) *ItemExternalColumnsColumnDefinitionItemRequestBuilder { +// urlTplParams := make(map[string]string) +// for idx, item := range m.pathParameters { +// urlTplParams[idx] = item +// } +// if id != "" { +// urlTplParams["columnDefinition%2Did"] = id +// } +// return NewItemExternalColumnsColumnDefinitionItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +// } +// +// Get retrieve properties and relationships for a [site][] resource.A **site** resource represents a team site in SharePoint. +// [Find more info here] +// +// [Find more info here]: https://docs.microsoft.com/graph/api/site-get?view=graph-rest-1.0 +// +//nolint:wsl,revive,lll +func (m *SiteItemRequestBuilder) Get(ctx context.Context, requestConfiguration *SiteItemRequestBuilderGetRequestConfiguration) (msmodel.Siteable, error) { + requestInfo, err := m.CreateGetRequestInformation(ctx, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, msmodel.CreateSiteFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(msmodel.Siteable), nil +} + +//nolint:lll +// GetActivitiesByIntervalWithStartDateTimeWithEndDateTimeWithInterval provides operations to call the getActivitiesByInterval method. +// GetApplicableContentTypesForListWithListId provides operations to call the getApplicableContentTypesForList method. +// GetByPathWithPath provides operations to call the getByPath method. +// InformationProtection provides operations to manage the informationProtection property of the microsoft.graph.site entity. +// Items provides operations to manage the items property of the microsoft.graph.site entity. +// ItemsById provides operations to manage the items property of the microsoft.graph.site entity. +// Lists provides operations to manage the lists property of the microsoft.graph.site entity. +// ListsById provides operations to manage the lists property of the microsoft.graph.site entity. +// Onenote provides operations to manage the onenote property of the microsoft.graph.site entity. +// Operations provides operations to manage the operations property of the microsoft.graph.site entity. +// OperationsById provides operations to manage the operations property of the microsoft.graph.site entity. + +// Pages provides operations to manage the pages property of the microsoft.graph.site entity. +func (m *SiteItemRequestBuilder) Pages() *ItemPagesRequestBuilder { + return NewItemPagesRequestBuilderInternal(m.pathParameters, m.requestAdapter) +} + +// PagesById provides operations to manage the pages property of the microsoft.graph.site entity. +// +//nolint:revive,wsl +func (m *SiteItemRequestBuilder) PagesById(id string) *ItemPagesSitePageItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["sitePage%2Did"] = id + } + return NewItemPagesSitePageItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} + +// Patch update entity in sites by key (id) +// +//nolint:wsl,revive,lll +func (m *SiteItemRequestBuilder) Patch( + ctx context.Context, + body msmodel.Siteable, + requestConfiguration *SiteItemRequestBuilderPatchRequestConfiguration, +) (msmodel.Siteable, error) { + requestInfo, err := m.CreatePatchRequestInformation(ctx, body, requestConfiguration) + if err != nil { + return nil, err + } + errorMapping := i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f.ErrorMappings{ + "4XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + "5XX": i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0.CreateODataErrorFromDiscriminatorValue, + } + res, err := m.requestAdapter.Send(ctx, requestInfo, msmodel.CreateSiteFromDiscriminatorValue, errorMapping) + if err != nil { + return nil, err + } + if res == nil { + return nil, nil + } + return res.(msmodel.Siteable), nil +} + +// Permissions provides operations to manage the permissions property of the microsoft.graph.site entity. +// PermissionsById provides operations to manage the permissions property of the microsoft.graph.site entity. +// Sites provides operations to manage the sites property of the microsoft.graph.site entity. +// func (m *SiteItemRequestBuilder) Sites() +// SitesById provides operations to manage the sites property of the microsoft.graph.site entity. +// +//nolint:revive,wsl +func (m *SiteItemRequestBuilder) SitesById(id string) *ItemSitesSiteItemRequestBuilder { + urlTplParams := make(map[string]string) + for idx, item := range m.pathParameters { + urlTplParams[idx] = item + } + if id != "" { + urlTplParams["site%2Did1"] = id + } + return NewItemSitesSiteItemRequestBuilderInternal(urlTplParams, m.requestAdapter) +} + +// TermStore provides operations to manage the termStore property of the microsoft.graph.site entity. diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index ce16a5a4d..698ee8527 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -336,12 +336,12 @@ func checkContact( testEmptyOrEqual(t, expected.GetAssistantName(), got.GetAssistantName(), "AssistantName") testEmptyOrEqual(t, expected.GetBirthday(), got.GetBirthday(), "Birthday") - - assert.Equal(t, expected.GetBusinessAddress(), got.GetBusinessAddress()) - - testEmptyOrEqual(t, expected.GetBusinessHomePage(), got.GetBusinessHomePage(), "BusinessHomePage") - - assert.Equal(t, expected.GetBusinessPhones(), got.GetBusinessPhones()) + // Not present in msgraph-beta-sdk/models + // assert.Equal(t, expected.GetBusinessAddress(), got.GetBusinessAddress()) + // Not present in msgraph-beta-sdk/models + // testEmptyOrEqual(t, expected.GetBusinessHomePage(), got.GetBusinessHomePage(), "BusinessHomePage") + // Not present in msgraph-beta-sdk/models + // assert.Equal(t, expected.GetBusinessPhones(), got.GetBusinessPhones()) assert.Equal(t, expected.GetCategories(), got.GetCategories()) @@ -365,9 +365,10 @@ func checkContact( testEmptyOrEqual(t, expected.GetGivenName(), got.GetGivenName(), "GivenName") - assert.Equal(t, expected.GetHomeAddress(), got.GetHomeAddress()) - - assert.Equal(t, expected.GetHomePhones(), got.GetHomePhones()) + // Not present in msgraph-beta-sdk/models + // assert.Equal(t, expected.GetHomeAddress(), got.GetHomeAddress()) + // Not present in msgraph-beta-sdk/models + // assert.Equal(t, expected.GetHomePhones(), got.GetHomePhones()) // Skip CreatedDateTime as it's tied to this specific instance of the item. @@ -383,13 +384,14 @@ func checkContact( testEmptyOrEqual(t, expected.GetMiddleName(), got.GetMiddleName(), "MiddleName") - testEmptyOrEqual(t, expected.GetMobilePhone(), got.GetMobilePhone(), "MobilePhone") + // Not present in msgraph-beta-sdk/models + // testEmptyOrEqual(t, expected.GetMobilePhone(), got.GetMobilePhone(), "MobilePhone") testEmptyOrEqual(t, expected.GetNickName(), got.GetNickName(), "NickName") testEmptyOrEqual(t, expected.GetOfficeLocation(), got.GetOfficeLocation(), "OfficeLocation") - - assert.Equal(t, expected.GetOtherAddress(), got.GetOtherAddress()) + // Not present in msgraph-beta-sdk/models + // assert.Equal(t, expected.GetOtherAddress(), got.GetOtherAddress()) // Skip ParentFolderId as it's tied to this specific instance of the item. diff --git a/src/internal/connector/sharepoint/pageInfo.go b/src/internal/connector/sharepoint/pageInfo.go index 2c11863cb..40fd69404 100644 --- a/src/internal/connector/sharepoint/pageInfo.go +++ b/src/internal/connector/sharepoint/pageInfo.go @@ -3,13 +3,14 @@ package sharepoint import ( "time" + "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" "github.com/alcionai/corso/src/pkg/backup/details" ) // sharePointPageInfo propagates metadata from the SharePoint Page data type // into searchable content. // Page Details: https://learn.microsoft.com/en-us/graph/api/resources/sitepage?view=graph-rest-beta -func sharePointPageInfo(page SitePageable, size int64) *details.SharePointInfo { +func sharePointPageInfo(page models.SitePageable, size int64) *details.SharePointInfo { var ( name, webURL string created, modified time.Time diff --git a/src/internal/connector/sharepoint/pageInfo_test.go b/src/internal/connector/sharepoint/pageInfo_test.go index 6ea070fbe..81b9f27fb 100644 --- a/src/internal/connector/sharepoint/pageInfo_test.go +++ b/src/internal/connector/sharepoint/pageInfo_test.go @@ -5,26 +5,27 @@ import ( "github.com/stretchr/testify/assert" + "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" "github.com/alcionai/corso/src/pkg/backup/details" ) func (suite *SharePointInfoSuite) TestSharePointInfo_Pages() { tests := []struct { name string - pageAndDeets func() (SitePageable, *details.SharePointInfo) + pageAndDeets func() (models.SitePageable, *details.SharePointInfo) }{ { name: "Empty Page", - pageAndDeets: func() (SitePageable, *details.SharePointInfo) { + pageAndDeets: func() (models.SitePageable, *details.SharePointInfo) { deets := &details.SharePointInfo{ItemType: details.SharePointItem} - return NewSitePage(), deets + return models.NewSitePage(), deets }, }, { name: "Only Name", - pageAndDeets: func() (SitePageable, *details.SharePointInfo) { + pageAndDeets: func() (models.SitePageable, *details.SharePointInfo) { title := "Blank Page" - sPage := NewSitePage() + sPage := models.NewSitePage() sPage.SetTitle(&title) deets := &details.SharePointInfo{ ItemType: details.SharePointItem, diff --git a/src/internal/connector/sharepoint/site_page.go b/src/internal/connector/sharepoint/site_page.go deleted file mode 100644 index 56bf7ad27..000000000 --- a/src/internal/connector/sharepoint/site_page.go +++ /dev/null @@ -1,187 +0,0 @@ -package sharepoint - -import ( - kioser "github.com/microsoft/kiota-abstractions-go/serialization" - "github.com/microsoftgraph/msgraph-sdk-go/models" -) - -// SitePage provides operations to manage the minimal creation of a Site Page. -// Altered from original: github.com/microsoftgraph/msgraph-beta-sdk-go/models -// TODO: remove when Issue #2086 resolved -type SitePage struct { - models.BaseItem - // Indicates the layout of the content in a given SharePoint page, including horizontal sections and vertical section - // canvasLayout models.CanvasLayoutable - // Inherited from baseItem. - contentType models.ContentTypeInfoable - // The name of the page layout of the page. - // The possible values are: microsoftReserved, article, home, unknownFutureValue. - // pageLayout *models.PageLayoutType - // Indicates the promotion kind of the sitePage. The possible values are: - // microsoftReserved, page, newsPost, unknownFutureValue. - // promotionKind *models.PagePromotionType - // The publishing status and the MM.mm version of the page. - publishingState models.PublicationFacetable - // Reactions information for the page. - // reactions models.ReactionsFacetable - // Determines whether or not to show comments at the bottom of the page. - showComments *bool - // Determines whether or not to show recommended pages at the bottom of the page. - showRecommendedPages *bool - // Url of the sitePage's thumbnail image - //revive:disable:var-naming - thumbnailWebUrl *string - //revive:enable:var-naming - // Title of the sitePage. - title *string -} - -// Title area on the SharePoint page. -// titleArea models.TitleAreaable -// Collection of webparts on the SharePoint page -// webParts []models.WebPartable - -var _ SitePageable = &SitePage{} - -// NewSitePage instantiates a new sitePage and sets the default values. -func NewSitePage() *SitePage { - m := &SitePage{ - BaseItem: *models.NewBaseItem(), - } - odataTypeValue := "#microsoft.graph.sitePage" - m.SetOdataType(&odataTypeValue) - - return m -} - -// CreateSitePageFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateSitePageFromDiscriminatorValue(parseNode kioser.ParseNode) (kioser.Parsable, error) { - return NewSitePage(), nil -} - -// GetContentType gets the contentType property value. Inherited from baseItem. -func (m *SitePage) GetContentType() models.ContentTypeInfoable { - return m.contentType -} - -// GetFieldDeserializers the deserialization information for the current model -// Altered from original. -func (m *SitePage) GetFieldDeserializers() map[string]func(kioser.ParseNode) error { - res := m.BaseItem.GetFieldDeserializers() - - return res -} - -// GetPublishingState gets the publishingState property value. The publishing status and the MM.mm version of the page. -func (m *SitePage) GetPublishingState() models.PublicationFacetable { - return m.publishingState -} - -// GetShowComments gets the showComments property value. -// Determines whether or not to show comments at the bottom of the page. -func (m *SitePage) GetShowComments() *bool { - return m.showComments -} - -// GetShowRecommendedPages gets the showRecommendedPages property value. -// Determines whether or not to show recommended pages at the bottom of the page. -func (m *SitePage) GetShowRecommendedPages() *bool { - return m.showRecommendedPages -} - -// GetThumbnailWebUrl gets the thumbnailWebUrl property value. Url of the sitePage's thumbnail image -// -//revive:disable:var-naming -func (m *SitePage) GetThumbnailWebUrl() *string { - return m.thumbnailWebUrl -} - -// GetTitle gets the title property value. Title of the sitePage. -func (m *SitePage) GetTitle() *string { - return m.title -} - -// Serialize serializes information the current object -func (m *SitePage) Serialize(writer kioser.SerializationWriter) error { - err := m.BaseItem.Serialize(writer) - if err != nil { - return err - } - - if m.GetContentType() != nil { - err = writer.WriteObjectValue("contentType", m.GetContentType()) - if err != nil { - return err - } - } - - if m.GetPublishingState() != nil { - err = writer.WriteObjectValue("publishingState", m.GetPublishingState()) - if err != nil { - return err - } - } - { - err = writer.WriteBoolValue("showComments", m.GetShowComments()) - if err != nil { - return err - } - } - { - err = writer.WriteBoolValue("showRecommendedPages", m.GetShowRecommendedPages()) - if err != nil { - return err - } - } - { - err = writer.WriteStringValue("thumbnailWebUrl", m.GetThumbnailWebUrl()) - if err != nil { - return err - } - } - { - err = writer.WriteStringValue("title", m.GetTitle()) - if err != nil { - return err - } - } - - return nil -} - -// SetContentType sets the contentType property value. Inherited from baseItem. -func (m *SitePage) SetContentType(value models.ContentTypeInfoable) { - m.contentType = value -} - -// SetPublishingState sets the publishingState property value. The publishing status and the MM.mm version of the page. -func (m *SitePage) SetPublishingState(value models.PublicationFacetable) { - m.publishingState = value -} - -// SetShowComments sets the showComments property value. -// Determines whether or not to show comments at the bottom of the page. -func (m *SitePage) SetShowComments(value *bool) { - m.showComments = value -} - -// SetShowRecommendedPages sets the showRecommendedPages property value. -// Determines whether or not to show recommended pages at the bottom of the page. -func (m *SitePage) SetShowRecommendedPages(value *bool) { - m.showRecommendedPages = value -} - -// SetThumbnailWebUrl sets the thumbnailWebUrl property value. -// Url of the sitePage's thumbnail image -// -//revive:disable:var-naming -func (m *SitePage) SetThumbnailWebUrl(value *string) { - m.thumbnailWebUrl = value -} - -//revive:enable:var-naming - -// SetTitle sets the title property value. Title of the sitePage. -func (m *SitePage) SetTitle(value *string) { - m.title = value -} diff --git a/src/internal/connector/sharepoint/site_pageable.go b/src/internal/connector/sharepoint/site_pageable.go deleted file mode 100644 index d0626321b..000000000 --- a/src/internal/connector/sharepoint/site_pageable.go +++ /dev/null @@ -1,24 +0,0 @@ -package sharepoint - -import ( - "github.com/microsoft/kiota-abstractions-go/serialization" - "github.com/microsoftgraph/msgraph-sdk-go/models" -) - -// SitePageable adjusted from msgraph-beta-sdk-go for temporary testing -type SitePageable interface { - models.BaseItemable - serialization.Parsable - GetContentType() models.ContentTypeInfoable - GetPublishingState() models.PublicationFacetable - GetShowComments() *bool - GetShowRecommendedPages() *bool - GetThumbnailWebUrl() *string - GetTitle() *string - SetContentType(value models.ContentTypeInfoable) - SetPublishingState(value models.PublicationFacetable) - SetShowComments(value *bool) - SetShowRecommendedPages(value *bool) - SetThumbnailWebUrl(value *string) - SetTitle(value *string) -} From c270536f731472ab24ab7a74f6a16ade6f421367 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 13:21:01 -0700 Subject: [PATCH 13/46] add version to clues, log cli command and flags (#2331) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual --- src/cli/cli.go | 28 +++++++++++++++++++++++++--- src/cli/config/config.go | 24 +++++++++++------------- src/go.mod | 4 ++-- src/internal/version/version.go | 26 ++++++++++++++++++++++++++ src/pkg/logger/logger.go | 3 ++- 5 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/cli/cli.go b/src/cli/cli.go index f06354f0b..b67663d06 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -15,6 +15,7 @@ import ( "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/repo" "github.com/alcionai/corso/src/cli/restore" + "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/observe" "github.com/alcionai/corso/src/internal/version" "github.com/alcionai/corso/src/pkg/logger" @@ -31,7 +32,27 @@ var corsoCmd = &cobra.Command{ Short: "Free, Secure, Open-Source Backup for M365.", Long: `Free, Secure, and Open-Source Backup for Microsoft 365.`, RunE: handleCorsoCmd, - PersistentPreRunE: config.InitFunc(), + PersistentPreRunE: preRun, +} + +func preRun(cc *cobra.Command, args []string) error { + if err := config.InitFunc(cc, args); err != nil { + return err + } + + log := logger.Ctx(cc.Context()) + + flags := utils.GetPopulatedFlags(cc) + flagSl := make([]string, 0, len(flags)) + + // currently only tracking flag names to avoid pii leakage. + for f := range flags { + flagSl = append(flagSl, f) + } + + log.Infow("cli command", "command", cc.CommandPath(), "flags", flagSl, "version", version.CurrentVersion()) + + return nil } // Handler for flat calls to `corso`. @@ -39,7 +60,7 @@ var corsoCmd = &cobra.Command{ func handleCorsoCmd(cmd *cobra.Command, args []string) error { v, _ := cmd.Flags().GetBool("version") if v { - print.Outf(cmd.Context(), "Corso version: "+version.Version) + print.Outf(cmd.Context(), "Corso version: "+version.CurrentVersion()) return nil } @@ -62,7 +83,7 @@ func BuildCommandTree(cmd *cobra.Command) { cmd.PersistentFlags().SortFlags = false cmd.Flags().BoolP("version", "v", false, "current version info") - cmd.PersistentPostRunE = config.InitFunc() + cmd.PersistentPreRunE = preRun config.AddConfigFlags(cmd) logger.AddLoggingFlags(cmd) observe.AddProgressBarFlags(cmd) @@ -85,6 +106,7 @@ func BuildCommandTree(cmd *cobra.Command) { // Handle builds and executes the cli processor. func Handle() { + //nolint:forbidigo ctx := config.Seed(context.Background()) ctx = print.SetRootCmd(ctx, corsoCmd) observe.SeedWriter(ctx, print.StderrWriter(ctx), observe.PreloadFlags()) diff --git a/src/cli/config/config.go b/src/cli/config/config.go index dbcd21422..8f532abb6 100644 --- a/src/cli/config/config.go +++ b/src/cli/config/config.go @@ -77,20 +77,18 @@ func AddConfigFlags(cmd *cobra.Command) { // InitFunc provides a func that lazily initializes viper and // verifies that the configuration was able to read a file. -func InitFunc() func(*cobra.Command, []string) error { - return func(cmd *cobra.Command, args []string) error { - fp := configFilePathFlag - if len(fp) == 0 || fp == displayDefaultFP { - fp = configFilePath - } - - err := initWithViper(GetViper(cmd.Context()), fp) - if err != nil { - return err - } - - return Read(cmd.Context()) +func InitFunc(cmd *cobra.Command, args []string) error { + fp := configFilePathFlag + if len(fp) == 0 || fp == displayDefaultFP { + fp = configFilePath } + + err := initWithViper(GetViper(cmd.Context()), fp) + if err != nil { + return err + } + + return Read(cmd.Context()) } // initWithViper implements InitConfig, but takes in a viper diff --git a/src/go.mod b/src/go.mod index 1b05ec06b..a8054ab0e 100644 --- a/src/go.mod +++ b/src/go.mod @@ -13,6 +13,7 @@ require ( github.com/microsoft/kiota-abstractions-go v0.16.0 github.com/microsoft/kiota-authentication-azure-go v0.6.0 github.com/microsoft/kiota-http-go v0.13.0 + github.com/microsoft/kiota-serialization-form-go v0.2.0 github.com/microsoft/kiota-serialization-json-go v0.7.2 github.com/microsoftgraph/msgraph-sdk-go v0.53.0 github.com/microsoftgraph/msgraph-sdk-go-core v0.33.0 @@ -40,7 +41,6 @@ require ( github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/microsoft/kiota-serialization-form-go v0.2.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/spf13/afero v1.9.3 // indirect @@ -84,7 +84,7 @@ require ( github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/microsoft/kiota-serialization-text-go v0.6.0 // indirect + github.com/microsoft/kiota-serialization-text-go v0.6.0 github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/minio-go/v7 v7.0.45 // indirect github.com/minio/sha256-simd v1.0.0 // indirect diff --git a/src/internal/version/version.go b/src/internal/version/version.go index 4a07d07ba..6b1859a80 100644 --- a/src/internal/version/version.go +++ b/src/internal/version/version.go @@ -1,3 +1,29 @@ package version +import ( + "os/exec" + "strings" +) + var Version = "dev" + +func CurrentVersion() string { + if len(Version) == 0 || Version == "dev" { + c, b := exec.Command("git", "describe", "--tag"), new(strings.Builder) + c.Stdout = b + + if err := c.Run(); err != nil { + return "dev" + } + + s := strings.TrimRight(b.String(), "\n") + + if len(s) != 0 { + return "dev-" + s + } + + return "dev" + } + + return Version +} diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index f64febe2e..0d5ffe250 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -6,6 +6,7 @@ import ( "path/filepath" "time" + "github.com/alcionai/clues" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.uber.org/zap" @@ -264,7 +265,7 @@ func Ctx(ctx context.Context) *zap.SugaredLogger { return singleton(levelOf(llFlag), defaultLogLocation()) } - return l.(*zap.SugaredLogger) + return l.(*zap.SugaredLogger).With(clues.Slice(ctx)...) } // transforms the llevel flag value to a logLevel enum From 013eeab7cb7a872c50b181811b05d2fe339ecf72 Mon Sep 17 00:00:00 2001 From: Danny Date: Tue, 31 Jan 2023 15:46:31 -0500 Subject: [PATCH 14/46] GC: Add Service to `graph/betasdk` package (#2298) ## Description Creates Beta Service for betasdk package. Abstraction loosely complies with the methods found in `graph.Servicer()`. Will aid when Sharepoint functionality is supported in v1.0 and `betasdk` package is removed. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :sunflower: Feature ## Issue(s) - related to * #2174 ## Test Plan - [x] :muscle: Manual --- .../connector/discovery/api/beta_service.go | 43 ++++++++++++++++ .../discovery/api/beta_service_test.go | 49 +++++++++++++++++++ .../connector/graph/betasdk/beta_client.go | 5 ++ 3 files changed, 97 insertions(+) create mode 100644 src/internal/connector/discovery/api/beta_service.go create mode 100644 src/internal/connector/discovery/api/beta_service_test.go diff --git a/src/internal/connector/discovery/api/beta_service.go b/src/internal/connector/discovery/api/beta_service.go new file mode 100644 index 000000000..df2b1533b --- /dev/null +++ b/src/internal/connector/discovery/api/beta_service.go @@ -0,0 +1,43 @@ +package api + +import ( + "github.com/alcionai/corso/src/internal/connector/graph/betasdk" + absser "github.com/microsoft/kiota-abstractions-go/serialization" + msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" + "github.com/pkg/errors" +) + +// Service wraps BetaClient's functionality. +// Abstraction created to comply loosely with graph.Servicer +// methods for ease of switching between v1.0 and beta connnectors +type Service struct { + client *betasdk.BetaClient +} + +func (s Service) Client() *betasdk.BetaClient { + return s.client +} + +func NewBetaService(adpt *msgraphsdk.GraphRequestAdapter) *Service { + return &Service{ + client: betasdk.NewBetaClient(adpt), + } +} + +// Seraialize writes an M365 parsable object into a byte array using the built-in +// application/json writer within the adapter. +func (s Service) Serialize(object absser.Parsable) ([]byte, error) { + writer, err := s.client.Adapter(). + GetSerializationWriterFactory(). + GetSerializationWriter("application/json") + if err != nil || writer == nil { + return nil, errors.Wrap(err, "creating json serialization writer") + } + + err = writer.WriteObjectValue("", object) + if err != nil { + return nil, errors.Wrap(err, "writeObjecValue serialization") + } + + return writer.GetSerializedContent() +} diff --git a/src/internal/connector/discovery/api/beta_service_test.go b/src/internal/connector/discovery/api/beta_service_test.go new file mode 100644 index 000000000..ad67b3877 --- /dev/null +++ b/src/internal/connector/discovery/api/beta_service_test.go @@ -0,0 +1,49 @@ +package api + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" + "github.com/alcionai/corso/src/internal/tester" +) + +type BetaUnitSuite struct { + suite.Suite +} + +func TestBetaUnitSuite(t *testing.T) { + suite.Run(t, new(BetaUnitSuite)) +} + +func (suite *BetaUnitSuite) TestBetaService_Adapter() { + t := suite.T() + a := tester.NewM365Account(t) + m365, err := a.M365Config() + require.NoError(t, err) + + adpt, err := graph.CreateAdapter( + m365.AzureTenantID, + m365.AzureClientID, + m365.AzureClientSecret, + ) + require.NoError(t, err) + + service := NewBetaService(adpt) + require.NotNil(t, service) + + testPage := models.NewSitePage() + name := "testFile" + desc := "working with parsing" + + testPage.SetName(&name) + testPage.SetDescription(&desc) + + byteArray, err := service.Serialize(testPage) + assert.NotEmpty(t, byteArray) + assert.NoError(t, err) +} diff --git a/src/internal/connector/graph/betasdk/beta_client.go b/src/internal/connector/graph/betasdk/beta_client.go index 515e1de95..ef77b8169 100644 --- a/src/internal/connector/graph/betasdk/beta_client.go +++ b/src/internal/connector/graph/betasdk/beta_client.go @@ -84,3 +84,8 @@ func (m *BetaClient) SitesById(id string) *i1a3c1a5501c5e41b7fd169f2d4c768dce9b0 } return i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411.NewSiteItemRequestBuilderInternal(urlTplParams, m.requestAdapter) } + +// Adapter() helper method to export Adapter for iterating +func (m *BetaClient) Adapter() *msgraphsdk.GraphRequestAdapter { + return m.requestAdapter +} From 498fc325a9149e22617515a06b6a0c0e36827613 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 14:22:53 -0700 Subject: [PATCH 15/46] store errors in operation (#2237) ## Description Adds the fault.Errors struct (now exported) to the operations base. stats.Errs is retained in the backup and restore wrappers to avoid breaking changes and allow for deserialization. We will continue to use the current error return until dependencies are fully updated to use Errors. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #1970 ## Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/internal/operations/backup.go | 3 +- .../operations/backup_integration_test.go | 6 ++++ src/internal/operations/backup_test.go | 2 +- src/internal/operations/operation.go | 20 +++++++---- src/internal/operations/restore.go | 2 +- src/internal/operations/restore_test.go | 8 +++-- src/pkg/backup/backup.go | 33 ++++++++++++++++--- src/pkg/backup/backup_test.go | 4 +++ src/pkg/repository/repository_load_test.go | 12 ++++--- 9 files changed, 69 insertions(+), 21 deletions(-) diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 247f9859b..b2a0c552c 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -44,7 +44,7 @@ type BackupOperation struct { // BackupResults aggregate the details of the result of the operation. type BackupResults struct { - stats.Errs + stats.Errs // deprecated in place of fault.Errors in the base operation. stats.ReadWrites stats.StartAndEndTime BackupID model.StableID `json:"backupID"` @@ -609,6 +609,7 @@ func (op *BackupOperation) createBackupModels( op.Selectors, op.Results.ReadWrites, op.Results.StartAndEndTime, + op.Errors, ) err = op.store.Put(ctx, model.BackupSchema, b) diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index c28159c1a..21d4009e0 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -153,6 +153,8 @@ func runAndCheckBackup( assert.Less(t, int64(0), bo.Results.BytesRead, "bytes read") assert.Less(t, int64(0), bo.Results.BytesUploaded, "bytes uploaded") assert.Equal(t, 1, bo.Results.ResourceOwners, "count of resource owners") + assert.NoError(t, bo.Errors.Err(), "incremental non-recoverable error") + assert.Empty(t, bo.Errors.Errs(), "incremental recoverable/iteration errors") assert.NoError(t, bo.Results.ReadErrors, "errors reading data") assert.NoError(t, bo.Results.WriteErrors, "errors writing data") assert.Equal(t, 1, mb.TimesCalled[events.BackupStart], "backup-start events") @@ -616,6 +618,8 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchange() { assert.Greater(t, bo.Results.BytesRead, incBO.Results.BytesRead, "incremental bytes read") assert.Greater(t, bo.Results.BytesUploaded, incBO.Results.BytesUploaded, "incremental bytes uploaded") assert.Equal(t, bo.Results.ResourceOwners, incBO.Results.ResourceOwners, "incremental backup resource owner") + assert.NoError(t, incBO.Errors.Err(), "incremental non-recoverable error") + assert.Empty(t, incBO.Errors.Errs(), "count incremental recoverable/iteration errors") assert.NoError(t, incBO.Results.ReadErrors, "incremental read errors") assert.NoError(t, incBO.Results.WriteErrors, "incremental write errors") assert.Equal(t, 1, incMB.TimesCalled[events.BackupStart], "incremental backup-start events") @@ -1039,6 +1043,8 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_exchangeIncrementals() { // +4 on read/writes to account for metadata: 1 delta and 1 path for each type. assert.Equal(t, test.itemsWritten+4, incBO.Results.ItemsWritten, "incremental items written") assert.Equal(t, test.itemsRead+4, incBO.Results.ItemsRead, "incremental items read") + assert.NoError(t, incBO.Errors.Err(), "incremental non-recoverable error") + assert.Empty(t, incBO.Errors.Errs(), "incremental recoverable/iteration errors") assert.NoError(t, incBO.Results.ReadErrors, "incremental read errors") assert.NoError(t, incBO.Results.WriteErrors, "incremental write errors") assert.Equal(t, 1, incMB.TimesCalled[events.BackupStart], "incremental backup-start events") diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index 2a7a1ecce..f867e2a11 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -420,11 +420,11 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { assert.Equal(t, test.expectStatus.String(), op.Status.String(), "status") assert.Equal(t, test.stats.gc.Successful, op.Results.ItemsRead, "items read") - assert.Equal(t, test.stats.readErr, op.Results.ReadErrors, "read errors") assert.Equal(t, test.stats.k.TotalFileCount, op.Results.ItemsWritten, "items written") assert.Equal(t, test.stats.k.TotalHashedBytes, op.Results.BytesRead, "bytes read") assert.Equal(t, test.stats.k.TotalUploadedBytes, op.Results.BytesUploaded, "bytes written") assert.Equal(t, test.stats.resourceCount, op.Results.ResourceOwners, "resource owners") + assert.Equal(t, test.stats.readErr, op.Results.ReadErrors, "read errors") assert.Equal(t, test.stats.writeErr, op.Results.WriteErrors, "write errors") assert.Equal(t, now, op.Results.StartedAt, "started at") assert.Less(t, now, op.Results.CompletedAt, "completed at") diff --git a/src/internal/operations/operation.go b/src/internal/operations/operation.go index 24391997e..32122d682 100644 --- a/src/internal/operations/operation.go +++ b/src/internal/operations/operation.go @@ -13,6 +13,7 @@ import ( "github.com/alcionai/corso/src/internal/observe" "github.com/alcionai/corso/src/pkg/account" "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/selectors" "github.com/alcionai/corso/src/pkg/store" ) @@ -52,9 +53,11 @@ const ( // Specific processes (eg: backups, restores, etc) are expected to wrap operation // with process specific details. type operation struct { - CreatedAt time.Time `json:"createdAt"` // datetime of the operation's creation - Options control.Options `json:"options"` - Status opStatus `json:"status"` + CreatedAt time.Time `json:"createdAt"` + + Errors *fault.Errors `json:"errors"` + Options control.Options `json:"options"` + Status opStatus `json:"status"` bus events.Eventer kopia *kopia.Wrapper @@ -69,11 +72,14 @@ func newOperation( ) operation { return operation{ CreatedAt: time.Now(), + Errors: fault.New(opts.FailFast), Options: opts, - bus: bus, - kopia: kw, - store: sw, - Status: InProgress, + + bus: bus, + kopia: kw, + store: sw, + + Status: InProgress, } } diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index 8ddaa8d29..db5ca9a93 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -44,7 +44,7 @@ type RestoreOperation struct { // RestoreResults aggregate the details of the results of the operation. type RestoreResults struct { - stats.Errs + stats.Errs // deprecated in place of fault.Errors in the base operation. stats.ReadWrites stats.StartAndEndTime } diff --git a/src/internal/operations/restore_test.go b/src/internal/operations/restore_test.go index 973a178b4..9bf21b806 100644 --- a/src/internal/operations/restore_test.go +++ b/src/internal/operations/restore_test.go @@ -104,10 +104,10 @@ func (suite *RestoreOpSuite) TestRestoreOperation_PersistResults() { assert.Equal(t, test.expectStatus.String(), op.Status.String(), "status") assert.Equal(t, len(test.stats.cs), op.Results.ItemsRead, "items read") - assert.Equal(t, test.stats.readErr, op.Results.ReadErrors, "read errors") assert.Equal(t, test.stats.gc.Successful, op.Results.ItemsWritten, "items written") assert.Equal(t, test.stats.bytesRead.NumBytes, op.Results.BytesRead, "resource owners") assert.Equal(t, test.stats.resourceCount, op.Results.ResourceOwners, "resource owners") + assert.Equal(t, test.stats.readErr, op.Results.ReadErrors, "read errors") assert.Equal(t, test.stats.writeErr, op.Results.WriteErrors, "write errors") assert.Equal(t, now, op.Results.StartedAt, "started at") assert.Less(t, now, op.Results.CompletedAt, "completed at") @@ -293,8 +293,10 @@ func (suite *RestoreOpIntegrationSuite) TestRestore_Run() { assert.Less(t, 0, ro.Results.ItemsWritten, "restored items written") assert.Less(t, int64(0), ro.Results.BytesRead, "bytes read") assert.Equal(t, 1, ro.Results.ResourceOwners, "resource Owners") - assert.Zero(t, ro.Results.ReadErrors, "errors while reading restore data") - assert.Zero(t, ro.Results.WriteErrors, "errors while writing restore data") + assert.NoError(t, ro.Errors.Err(), "non-recoverable error") + assert.Empty(t, ro.Errors.Errs(), "recoverable errors") + assert.NoError(t, ro.Results.ReadErrors, "errors while reading restore data") + assert.NoError(t, ro.Results.WriteErrors, "errors while writing restore data") assert.Equal(t, suite.numItems, ro.Results.ItemsWritten, "backup and restore wrote the same num of items") assert.Equal(t, 1, mb.TimesCalled[events.RestoreStart], "restore-start events") assert.Equal(t, 1, mb.TimesCalled[events.RestoreEnd], "restore-end events") diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index 6d73498eb..d0d9ddffd 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -10,6 +10,7 @@ import ( "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -31,8 +32,11 @@ type Backup struct { // Selector used in this operation Selector selectors.Selector `json:"selectors"` + // Errors contains all errors aggregated during a backup operation. + Errors fault.ErrorsData `json:"errors"` + // stats are embedded so that the values appear as top-level properties - stats.Errs + stats.Errs // Deprecated, replaced with Errors. stats.ReadWrites stats.StartAndEndTime } @@ -46,6 +50,7 @@ func New( selector selectors.Selector, rw stats.ReadWrites, se stats.StartAndEndTime, + errs *fault.Errors, ) *Backup { return &Backup{ BaseModel: model.BaseModel{ @@ -59,6 +64,7 @@ func New( DetailsID: detailsID, Status: status, Selector: selector, + Errors: errs.Data(), ReadWrites: rw, StartAndEndTime: se, } @@ -102,7 +108,7 @@ type Printable struct { func (b Backup) MinimumPrintable() any { return Printable{ ID: b.ID, - ErrorCount: support.GetNumberOfErrors(b.ReadErrors) + support.GetNumberOfErrors(b.WriteErrors), + ErrorCount: b.errorCount(), StartedAt: b.StartedAt, Status: b.Status, Version: "0", @@ -125,8 +131,7 @@ func (b Backup) Headers() []string { // Values returns the values matching the Headers list for printing // out to a terminal in a columnar display. func (b Backup) Values() []string { - errCount := support.GetNumberOfErrors(b.ReadErrors) + support.GetNumberOfErrors(b.WriteErrors) - status := fmt.Sprintf("%s (%d errors)", b.Status, errCount) + status := fmt.Sprintf("%s (%d errors)", b.Status, b.errorCount()) return []string{ common.FormatTabularDisplayTime(b.StartedAt), @@ -135,3 +140,23 @@ func (b Backup) Values() []string { b.Selector.DiscreteOwner, } } + +func (b Backup) errorCount() int { + var errCount int + + // current tracking + if b.ReadErrors != nil || b.WriteErrors != nil { + return support.GetNumberOfErrors(b.ReadErrors) + support.GetNumberOfErrors(b.WriteErrors) + } + + // future tracking + if b.Errors.Err != nil || len(b.Errors.Errs) > 0 { + if b.Errors.Err != nil { + errCount++ + } + + errCount += len(b.Errors.Errs) + } + + return errCount +} diff --git a/src/pkg/backup/backup_test.go b/src/pkg/backup/backup_test.go index 1ddf4f4a2..8fbf8c62f 100644 --- a/src/pkg/backup/backup_test.go +++ b/src/pkg/backup/backup_test.go @@ -13,6 +13,7 @@ import ( "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/stats" "github.com/alcionai/corso/src/pkg/backup" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -40,6 +41,9 @@ func stubBackup(t time.Time) backup.Backup { DetailsID: "details", Status: "status", Selector: sel.Selector, + Errors: fault.ErrorsData{ + Errs: []error{errors.New("read"), errors.New("write")}, + }, Errs: stats.Errs{ ReadErrors: errors.New("1"), WriteErrors: errors.New("1"), diff --git a/src/pkg/repository/repository_load_test.go b/src/pkg/repository/repository_load_test.go index 744b57449..36714df71 100644 --- a/src/pkg/repository/repository_load_test.go +++ b/src/pkg/repository/repository_load_test.go @@ -184,8 +184,10 @@ func runBackupLoadTest( assert.Less(t, 0, b.Results.ItemsWritten, "items written") assert.Less(t, int64(0), b.Results.BytesUploaded, "bytes uploaded") assert.Equal(t, len(users), b.Results.ResourceOwners, "resource owners") - assert.Zero(t, b.Results.ReadErrors, "read errors") - assert.Zero(t, b.Results.WriteErrors, "write errors") + assert.NoError(t, b.Errors.Err(), "non-recoverable error") + assert.Empty(t, b.Errors.Errs(), "recoverable errors") + assert.NoError(t, b.Results.ReadErrors, "read errors") + assert.NoError(t, b.Results.WriteErrors, "write errors") }) } @@ -290,8 +292,10 @@ func doRestoreLoadTest( assert.Less(t, 0, r.Results.ItemsRead, "items read") assert.Less(t, 0, r.Results.ItemsWritten, "items written") assert.Equal(t, len(users), r.Results.ResourceOwners, "resource owners") - assert.Zero(t, r.Results.ReadErrors, "read errors") - assert.Zero(t, r.Results.WriteErrors, "write errors") + assert.NoError(t, r.Errors.Err(), "non-recoverable error") + assert.Empty(t, r.Errors.Errs(), "recoverable errors") + assert.NoError(t, r.Results.ReadErrors, "read errors") + assert.NoError(t, r.Results.WriteErrors, "write errors") assert.Equal(t, expectItemCount, r.Results.ItemsWritten, "backup and restore wrote the same count of items") ensureAllUsersInDetails(t, users, ds, "restore", name) From f3b2e9a632c55804916f69fbcda157143e34b8f2 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Wed, 1 Feb 2023 03:27:38 +0530 Subject: [PATCH 16/46] Retry handling for delta queries in Exchange (#2328) ## Description Added retry handling for delta queries in OneDrive. Also, bumping time timeout for graph api calls from 90s to 3m as we were seeing client timeouts for graph api calls. ~Haven't added retry for every request in exchange as I'm hoping https://github.com/alcionai/corso/issues/2287 will be a better way to handle this.~ ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [ ] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * # ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- CHANGELOG.md | 1 + src/internal/connector/exchange/api/api.go | 24 ++ .../connector/exchange/api/contacts.go | 51 +++- src/internal/connector/exchange/api/events.go | 52 +++- src/internal/connector/exchange/api/mail.go | 41 ++- src/internal/connector/graph/errors.go | 24 +- src/internal/connector/graph/errors_test.go | 248 ++++++++++++++++++ src/internal/connector/graph/service.go | 4 +- src/internal/connector/graph/service_test.go | 2 +- src/internal/connector/onedrive/collection.go | 10 +- src/internal/connector/onedrive/item.go | 4 + 11 files changed, 424 insertions(+), 37 deletions(-) create mode 100644 src/internal/connector/graph/errors_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b18a4d04..b96aca9e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Document Corso's fault-tolerance and restartability features +- Add retries on timeouts and status code 500 for Exchange ## [v0.2.0] (alpha) - 2023-1-29 diff --git a/src/internal/connector/exchange/api/api.go b/src/internal/connector/exchange/api/api.go index c4858c5c1..c47cc9b5b 100644 --- a/src/internal/connector/exchange/api/api.go +++ b/src/internal/connector/exchange/api/api.go @@ -10,6 +10,7 @@ import ( "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/pkg/account" ) @@ -153,3 +154,26 @@ func HasAttachments(body models.ItemBodyable) bool { return strings.Contains(content, "src=\"cid:") } + +// Run a function with retries +func runWithRetry(run func() error) error { + var err error + + for i := 0; i < numberOfRetries; i++ { + err = run() + if err == nil { + return nil + } + + // only retry on timeouts and 500-internal-errors. + if !(graph.IsErrTimeout(err) || graph.IsInternalServerError(err)) { + break + } + + if i < numberOfRetries { + time.Sleep(time.Duration(3*(i+2)) * time.Second) + } + } + + return support.ConnectorStackErrorTraceWrap(err, "maximum retries or unretryable") +} diff --git a/src/internal/connector/exchange/api/contacts.go b/src/internal/connector/exchange/api/contacts.go index 0db1e964c..33f05d2a3 100644 --- a/src/internal/connector/exchange/api/contacts.go +++ b/src/internal/connector/exchange/api/contacts.go @@ -61,7 +61,16 @@ func (c Contacts) GetItem( ctx context.Context, user, itemID string, ) (serialization.Parsable, *details.ExchangeInfo, error) { - cont, err := c.stable.Client().UsersById(user).ContactsById(itemID).Get(ctx, nil) + var ( + cont models.Contactable + err error + ) + + err = runWithRetry(func() error { + cont, err = c.stable.Client().UsersById(user).ContactsById(itemID).Get(ctx, nil) + return err + }) + if err != nil { return nil, nil, err } @@ -81,7 +90,14 @@ func (c Contacts) GetAllContactFolderNamesForUser( return nil, err } - return c.stable.Client().UsersById(user).ContactFolders().Get(ctx, options) + var resp models.ContactFolderCollectionResponseable + + err = runWithRetry(func() error { + resp, err = c.stable.Client().UsersById(user).ContactFolders().Get(ctx, options) + return err + }) + + return resp, err } func (c Contacts) GetContainerByID( @@ -93,10 +109,14 @@ func (c Contacts) GetContainerByID( return nil, errors.Wrap(err, "options for contact folder") } - return c.stable.Client(). - UsersById(userID). - ContactFoldersById(dirID). - Get(ctx, ofcf) + var resp models.ContactFolderable + + err = runWithRetry(func() error { + resp, err = c.stable.Client().UsersById(userID).ContactFoldersById(dirID).Get(ctx, ofcf) + return err + }) + + return resp, err } // EnumerateContainers iterates through all of the users current @@ -117,6 +137,7 @@ func (c Contacts) EnumerateContainers( var ( errs *multierror.Error + resp models.ContactFolderCollectionResponseable fields = []string{"displayName", "parentFolderId"} ) @@ -131,7 +152,11 @@ func (c Contacts) EnumerateContainers( ChildFolders() for { - resp, err := builder.Get(ctx, ofcf) + err = runWithRetry(func() error { + resp, err = builder.Get(ctx, ofcf) + return err + }) + if err != nil { return errors.Wrap(err, support.ConnectorStackErrorTrace(err)) } @@ -174,7 +199,17 @@ type contactPager struct { } func (p *contactPager) getPage(ctx context.Context) (api.DeltaPageLinker, error) { - return p.builder.Get(ctx, p.options) + var ( + resp api.DeltaPageLinker + err error + ) + + err = runWithRetry(func() error { + resp, err = p.builder.Get(ctx, p.options) + return err + }) + + return resp, err } func (p *contactPager) setNext(nextLink string) { diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index e643c1f89..63545143d 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -73,7 +73,13 @@ func (c Events) GetContainerByID( return nil, errors.Wrap(err, "options for event calendar") } - cal, err := service.Client().UsersById(userID).CalendarsById(containerID).Get(ctx, ofc) + var cal models.Calendarable + + err = runWithRetry(func() error { + cal, err = service.Client().UsersById(userID).CalendarsById(containerID).Get(ctx, ofc) + return err + }) + if err != nil { return nil, err } @@ -86,7 +92,16 @@ func (c Events) GetItem( ctx context.Context, user, itemID string, ) (serialization.Parsable, *details.ExchangeInfo, error) { - event, err := c.stable.Client().UsersById(user).EventsById(itemID).Get(ctx, nil) + var ( + event models.Eventable + err error + ) + + err = runWithRetry(func() error { + event, err = c.stable.Client().UsersById(user).EventsById(itemID).Get(ctx, nil) + return err + }) + if err != nil { return nil, nil, err } @@ -128,7 +143,14 @@ func (c Client) GetAllCalendarNamesForUser( return nil, err } - return c.stable.Client().UsersById(user).Calendars().Get(ctx, options) + var resp models.CalendarCollectionResponseable + + err = runWithRetry(func() error { + resp, err = c.stable.Client().UsersById(user).Calendars().Get(ctx, options) + return err + }) + + return resp, err } // EnumerateContainers iterates through all of the users current @@ -147,7 +169,10 @@ func (c Events) EnumerateContainers( return err } - var errs *multierror.Error + var ( + resp models.CalendarCollectionResponseable + errs *multierror.Error + ) ofc, err := optionsForCalendars([]string{"name"}) if err != nil { @@ -157,7 +182,13 @@ func (c Events) EnumerateContainers( builder := service.Client().UsersById(userID).Calendars() for { - resp, err := builder.Get(ctx, ofc) + var err error + + err = runWithRetry(func() error { + resp, err = builder.Get(ctx, ofc) + return err + }) + if err != nil { return errors.Wrap(err, support.ConnectorStackErrorTrace(err)) } @@ -205,7 +236,16 @@ type eventPager struct { } func (p *eventPager) getPage(ctx context.Context) (api.DeltaPageLinker, error) { - resp, err := p.builder.Get(ctx, p.options) + var ( + resp api.DeltaPageLinker + err error + ) + + err = runWithRetry(func() error { + resp, err = p.builder.Get(ctx, p.options) + return err + }) + return resp, err } diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index bbac48a66..597676874 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -95,7 +95,14 @@ func (c Mail) GetContainerByID( return nil, errors.Wrap(err, "options for mail folder") } - return service.Client().UsersById(userID).MailFoldersById(dirID).Get(ctx, ofmf) + var resp graph.Container + + err = runWithRetry(func() error { + resp, err = service.Client().UsersById(userID).MailFoldersById(dirID).Get(ctx, ofmf) + return err + }) + + return resp, err } // GetItem retrieves a Messageable item. If the item contains an attachment, that @@ -104,7 +111,16 @@ func (c Mail) GetItem( ctx context.Context, user, itemID string, ) (serialization.Parsable, *details.ExchangeInfo, error) { - mail, err := c.stable.Client().UsersById(user).MessagesById(itemID).Get(ctx, nil) + var ( + mail models.Messageable + err error + ) + + err = runWithRetry(func() error { + mail, err = c.stable.Client().UsersById(user).MessagesById(itemID).Get(ctx, nil) + return err + }) + if err != nil { return nil, nil, err } @@ -154,6 +170,7 @@ func (c Mail) EnumerateContainers( } var ( + resp users.ItemMailFoldersDeltaResponseable errs *multierror.Error builder = service.Client(). UsersById(userID). @@ -162,7 +179,13 @@ func (c Mail) EnumerateContainers( ) for { - resp, err := builder.Get(ctx, nil) + var err error + + err = runWithRetry(func() error { + resp, err = builder.Get(ctx, nil) + return err + }) + if err != nil { return errors.Wrap(err, support.ConnectorStackErrorTrace(err)) } @@ -200,7 +223,17 @@ type mailPager struct { } func (p *mailPager) getPage(ctx context.Context) (api.DeltaPageLinker, error) { - return p.builder.Get(ctx, p.options) + var ( + page api.DeltaPageLinker + err error + ) + + err = runWithRetry(func() error { + page, err = p.builder.Get(ctx, p.options) + return err + }) + + return page, err } func (p *mailPager) setNext(nextLink string) { diff --git a/src/internal/connector/graph/errors.go b/src/internal/connector/graph/errors.go index c75e4a6cb..21116057d 100644 --- a/src/internal/connector/graph/errors.go +++ b/src/internal/connector/graph/errors.go @@ -17,6 +17,7 @@ import ( // --------------------------------------------------------------------------- const ( + errCodeActivityLimitReached = "activityLimitReached" errCodeItemNotFound = "ErrorItemNotFound" errCodeEmailFolderNotFound = "ErrorSyncFolderNotFound" errCodeResyncRequired = "ResyncRequired" @@ -31,8 +32,10 @@ var ( // normally the graph client will catch this for us, but in case we // run our own client Do(), we need to translate it to a timeout type // failure locally. - Err429TooManyRequests = errors.New("429 too many requests") - Err503ServiceUnavailable = errors.New("503 Service Unavailable") + Err429TooManyRequests = errors.New("429 too many requests") + Err503ServiceUnavailable = errors.New("503 Service Unavailable") + Err504GatewayTimeout = errors.New("504 Gateway Timeout") + Err500InternalServerError = errors.New("500 Internal Server Error") ) // The folder or item was deleted between the time we identified @@ -113,6 +116,10 @@ func IsErrThrottled(err error) bool { return true } + if hasErrorCode(err, errCodeActivityLimitReached) { + return true + } + e := ErrThrottled{} return errors.As(err, &e) @@ -135,21 +142,18 @@ func IsErrUnauthorized(err error) bool { return errors.As(err, &e) } -type ErrServiceUnavailable struct { +type ErrInternalServerError struct { common.Err } -func IsSericeUnavailable(err error) bool { - if errors.Is(err, Err503ServiceUnavailable) { +func IsInternalServerError(err error) bool { + if errors.Is(err, Err500InternalServerError) { return true } - e := ErrUnauthorized{} - if errors.As(err, &e) { - return true - } + e := ErrInternalServerError{} - return true + return errors.As(err, &e) } // --------------------------------------------------------------------------- diff --git a/src/internal/connector/graph/errors_test.go b/src/internal/connector/graph/errors_test.go new file mode 100644 index 000000000..c7f889b83 --- /dev/null +++ b/src/internal/connector/graph/errors_test.go @@ -0,0 +1,248 @@ +package graph + +import ( + "context" + "testing" + + "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/common" +) + +type GraphErrorsUnitSuite struct { + suite.Suite +} + +func TestGraphErrorsUnitSuite(t *testing.T) { + suite.Run(t, new(GraphErrorsUnitSuite)) +} + +func odErr(code string) *odataerrors.ODataError { + odErr := &odataerrors.ODataError{} + merr := odataerrors.MainError{} + merr.SetCode(&code) + odErr.SetError(&merr) + + return odErr +} + +func (suite *GraphErrorsUnitSuite) TestIsErrDeletedInFlight() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrDeletedInFlight{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "non-matching oDataErr", + err: odErr("fnords"), + expect: assert.False, + }, + { + name: "not-found oDataErr", + err: odErr(errCodeItemNotFound), + expect: assert.True, + }, + { + name: "sync-not-found oDataErr", + err: odErr(errCodeSyncFolderNotFound), + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsErrDeletedInFlight(test.err)) + }) + } +} + +func (suite *GraphErrorsUnitSuite) TestIsErrInvalidDelta() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrInvalidDelta{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "non-matching oDataErr", + err: odErr("fnords"), + expect: assert.False, + }, + { + name: "resync-required oDataErr", + err: odErr(errCodeResyncRequired), + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsErrInvalidDelta(test.err)) + }) + } +} + +func (suite *GraphErrorsUnitSuite) TestIsErrTimeout() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrTimeout{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "context deadline", + err: context.DeadlineExceeded, + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsErrTimeout(test.err)) + }) + } +} + +func (suite *GraphErrorsUnitSuite) TestIsErrThrottled() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrThrottled{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "is429", + err: Err429TooManyRequests, + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsErrThrottled(test.err)) + }) + } +} + +func (suite *GraphErrorsUnitSuite) TestIsErrUnauthorized() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrUnauthorized{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "is429", + err: Err401Unauthorized, + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsErrUnauthorized(test.err)) + }) + } +} + +func (suite *GraphErrorsUnitSuite) TestIsInternalServerError() { + table := []struct { + name string + err error + expect assert.BoolAssertionFunc + }{ + { + name: "nil", + err: nil, + expect: assert.False, + }, + { + name: "non-matching", + err: assert.AnError, + expect: assert.False, + }, + { + name: "as", + err: ErrInternalServerError{Err: *common.EncapsulateError(assert.AnError)}, + expect: assert.True, + }, + { + name: "is429", + err: Err500InternalServerError, + expect: assert.True, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + test.expect(t, IsInternalServerError(test.err)) + }) + } +} diff --git a/src/internal/connector/graph/service.go b/src/internal/connector/graph/service.go index 6c0e6dbc1..093995a42 100644 --- a/src/internal/connector/graph/service.go +++ b/src/internal/connector/graph/service.go @@ -149,7 +149,7 @@ func HTTPClient(opts ...option) *http.Client { middlewares := msgraphgocore.GetDefaultMiddlewaresWithOptions(&clientOptions) middlewares = append(middlewares, &LoggingMiddleware{}) httpClient := msgraphgocore.GetDefaultClient(&clientOptions, middlewares...) - httpClient.Timeout = time.Second * 90 + httpClient.Timeout = time.Minute * 3 (&clientConfig{}). populate(opts...). @@ -250,7 +250,6 @@ func (handler *LoggingMiddleware) Intercept( respDump, _ := httputil.DumpResponse(resp, false) metadata := []any{ - "idx", middlewareIndex, "method", req.Method, "status", resp.Status, "statusCode", resp.StatusCode, @@ -273,7 +272,6 @@ func (handler *LoggingMiddleware) Intercept( respDump, _ := httputil.DumpResponse(resp, true) metadata := []any{ - "idx", middlewareIndex, "method", req.Method, "status", resp.Status, "statusCode", resp.StatusCode, diff --git a/src/internal/connector/graph/service_test.go b/src/internal/connector/graph/service_test.go index 14bdc9c36..c2ef2d699 100644 --- a/src/internal/connector/graph/service_test.go +++ b/src/internal/connector/graph/service_test.go @@ -53,7 +53,7 @@ func (suite *GraphUnitSuite) TestHTTPClient() { name: "no options", opts: []option{}, check: func(t *testing.T, c *http.Client) { - assert.Equal(t, 90*time.Second, c.Timeout, "default timeout") + assert.Equal(t, 3*time.Minute, c.Timeout, "default timeout") }, }, { diff --git a/src/internal/connector/onedrive/collection.go b/src/internal/connector/onedrive/collection.go index a786de0ab..c4e1825bd 100644 --- a/src/internal/connector/onedrive/collection.go +++ b/src/internal/connector/onedrive/collection.go @@ -271,11 +271,11 @@ func (oc *Collection) populateItems(ctx context.Context) { continue - } else if !graph.IsErrTimeout(err) && !graph.IsErrThrottled(err) && !graph.IsSericeUnavailable(err) { - // TODO: graphAPI will provides headers that state the duration to wait - // in order to succeed again. The one second sleep won't cut it here. - // - // for all non-timeout, non-unauth, non-throttling errors, do not retry + } else if !graph.IsErrTimeout(err) && + !graph.IsInternalServerError(err) { + // Don't retry for non-timeout, on-unauth, as + // we are already retrying it in the default + // retry middleware break } diff --git a/src/internal/connector/onedrive/item.go b/src/internal/connector/onedrive/item.go index c4fd1b380..b1027de9d 100644 --- a/src/internal/connector/onedrive/item.go +++ b/src/internal/connector/onedrive/item.go @@ -105,6 +105,10 @@ func downloadItem(hc *http.Client, item models.DriveItemable) (*http.Response, e return resp, graph.Err401Unauthorized } + if resp.StatusCode == http.StatusInternalServerError { + return resp, graph.Err500InternalServerError + } + if resp.StatusCode == http.StatusServiceUnavailable { return resp, graph.Err503ServiceUnavailable } From 070b8fddeebac78f74f77c30917d10d6074910f4 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 15:25:02 -0700 Subject: [PATCH 17/46] update operation/backup errs (#2239) ## Description Begins updating operations/backup with the new error handling procedures. For backwards compatibility, errors are currently duplicated in the old stats.Errs and the new Errors struct. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #1970 ## Test Plan - [x] :zap: Unit test - [x] :green_heart: E2E --- src/internal/operations/backup.go | 84 ++++--- src/internal/operations/backup_test.go | 252 -------------------- src/internal/operations/manifests.go | 58 +++-- src/internal/operations/manifests_test.go | 277 +++++++++++++++++++++- src/pkg/fault/fault.go | 4 +- src/pkg/fault/mock/mock.go | 17 ++ 6 files changed, 374 insertions(+), 318 deletions(-) create mode 100644 src/pkg/fault/mock/mock.go diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index b2a0c552c..51e92181c 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -144,6 +144,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { err = op.persistResults(startTime, &opStats) if err != nil { + op.Errors.Fail(errors.Wrap(err, "persisting backup results")) return } @@ -153,7 +154,8 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { opStats.k.SnapshotID, backupDetails.Details()) if err != nil { - opStats.writeErr = err + op.Errors.Fail(errors.Wrap(err, "persisting backup")) + opStats.writeErr = op.Errors.Err() } }() @@ -164,21 +166,27 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { reasons, tenantID, uib, - ) + op.Errors) if err != nil { - opStats.readErr = errors.Wrap(err, "connecting to M365") + op.Errors.Fail(errors.Wrap(err, "collecting manifest heuristics")) + opStats.readErr = op.Errors.Err() + return opStats.readErr } gc, err := connectToM365(ctx, op.Selectors, op.account) if err != nil { - opStats.readErr = errors.Wrap(err, "connecting to M365") + op.Errors.Fail(errors.Wrap(err, "connecting to m365")) + opStats.readErr = op.Errors.Err() + return opStats.readErr } cs, err := produceBackupDataCollections(ctx, gc, op.Selectors, mdColls, op.Options) if err != nil { - opStats.readErr = errors.Wrap(err, "retrieving data to backup") + op.Errors.Fail(errors.Wrap(err, "retrieving data to backup")) + opStats.readErr = op.Errors.Err() + return opStats.readErr } @@ -194,7 +202,9 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { op.Results.BackupID, uib && canUseMetaData) if err != nil { - opStats.writeErr = errors.Wrap(err, "backing up service data") + op.Errors.Fail(errors.Wrap(err, "backing up service data")) + opStats.writeErr = op.Errors.Err() + return opStats.writeErr } @@ -211,12 +221,15 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { toMerge, backupDetails, ); err != nil { - opStats.writeErr = errors.Wrap(err, "merging backup details") + op.Errors.Fail(errors.Wrap(err, "merging backup details")) + opStats.writeErr = op.Errors.Err() + return opStats.writeErr } opStats.gc = gc.AwaitStatus() + // TODO(keepers): remove when fault.Errors handles all iterable error aggregation. if opStats.gc.ErrorCount > 0 { merr := multierror.Append(opStats.readErr, errors.Wrap(opStats.gc.Err, "retrieving data")) opStats.readErr = merr.ErrorOrNil() @@ -307,7 +320,9 @@ func selectorToReasons(sel selectors.Selector) []kopia.Reason { return reasons } -func builderFromReason(tenant string, r kopia.Reason) (*path.Builder, error) { +func builderFromReason(ctx context.Context, tenant string, r kopia.Reason) (*path.Builder, error) { + ctx = clues.Add(ctx, "category", r.Category.String()) + // This is hacky, but we want the path package to format the path the right // way (e.x. proper order for service, category, etc), but we don't care about // the folders after the prefix. @@ -319,12 +334,7 @@ func builderFromReason(tenant string, r kopia.Reason) (*path.Builder, error) { false, ) if err != nil { - return nil, errors.Wrapf( - err, - "building path for service %s category %s", - r.Service.String(), - r.Category.String(), - ) + return nil, clues.Wrap(err, "building path").WithMap(clues.Values(ctx)) } return p.ToBuilder().Dir(), nil @@ -367,7 +377,7 @@ func consumeBackupDataCollections( categories := map[string]struct{}{} for _, reason := range m.Reasons { - pb, err := builderFromReason(tenantID, reason) + pb, err := builderFromReason(ctx, tenantID, reason) if err != nil { return nil, nil, nil, errors.Wrap(err, "getting subtree paths for bases") } @@ -461,6 +471,8 @@ func mergeDetails( var addedEntries int for _, man := range mans { + mctx := clues.Add(ctx, "manifest_id", man.ID) + // For now skip snapshots that aren't complete. We will need to revisit this // when we tackle restartability. if len(man.IncompleteReason) > 0 { @@ -469,9 +481,11 @@ func mergeDetails( bID, ok := man.GetTag(kopia.TagBackupID) if !ok { - return errors.Errorf("no backup ID in snapshot manifest with ID %s", man.ID) + return clues.New("no backup ID in snapshot manifest").WithMap(clues.Values(mctx)) } + mctx = clues.Add(mctx, "manifest_backup_id", bID) + _, baseDeets, err := getBackupAndDetailsFromID( ctx, model.StableID(bID), @@ -479,18 +493,15 @@ func mergeDetails( detailsStore, ) if err != nil { - return errors.Wrapf(err, "backup fetching base details for backup %s", bID) + return clues.New("fetching base details for backup").WithMap(clues.Values(mctx)) } for _, entry := range baseDeets.Items() { rr, err := path.FromDataLayerPath(entry.RepoRef, true) if err != nil { - return errors.Wrapf( - err, - "parsing base item info path %s in backup %s", - entry.RepoRef, - bID, - ) + return clues.New("parsing base item info path"). + WithMap(clues.Values(mctx)). + With("repo_ref", entry.RepoRef) // todo: pii } // Although this base has an entry it may not be the most recent. Check @@ -513,11 +524,7 @@ func mergeDetails( // Fixup paths in the item. item := entry.ItemInfo if err := details.UpdateItem(&item, newPath); err != nil { - return errors.Wrapf( - err, - "updating item info for entry from backup %s", - bID, - ) + return clues.New("updating item details").WithMap(clues.Values(mctx)) } // TODO(ashmrtn): This may need updated if we start using this merge @@ -542,11 +549,9 @@ func mergeDetails( } if addedEntries != len(shortRefsFromPrevBackup) { - return errors.Errorf( - "incomplete migration of backup details: found %v of %v expected items", - addedEntries, - len(shortRefsFromPrevBackup), - ) + return clues.New("incomplete migration of backup details"). + WithMap(clues.Values(ctx)). + WithAll("item_count", addedEntries, "expected_item_count", len(shortRefsFromPrevBackup)) } return nil @@ -568,6 +573,7 @@ func (op *BackupOperation) persistResults( if opStats.readErr != nil || opStats.writeErr != nil { op.Status = Failed + // TODO(keepers): replace with fault.Errors handling. return multierror.Append( errors.New("errors prevented the operation from processing"), opStats.readErr, @@ -594,15 +600,18 @@ func (op *BackupOperation) createBackupModels( snapID string, backupDetails *details.Details, ) error { + ctx = clues.Add(ctx, "snapshot_id", snapID) + if backupDetails == nil { - return errors.New("no backup details to record") + return clues.New("no backup details to record").WithMap(clues.Values(ctx)) } detailsID, err := detailsStore.WriteBackupDetails(ctx, backupDetails) if err != nil { - return errors.Wrap(err, "creating backupdetails model") + return clues.Wrap(err, "creating backupDetails model").WithMap(clues.Values(ctx)) } + ctx = clues.Add(ctx, "details_id", detailsID) b := backup.New( snapID, detailsID, op.Status.String(), op.Results.BackupID, @@ -612,9 +621,8 @@ func (op *BackupOperation) createBackupModels( op.Errors, ) - err = op.store.Put(ctx, model.BackupSchema, b) - if err != nil { - return errors.Wrap(err, "creating backup model") + if err = op.store.Put(ctx, model.BackupSchema, b); err != nil { + return clues.Wrap(err, "creating backup model").WithMap(clues.Values(ctx)) } dur := op.Results.CompletedAt.Sub(op.Results.StartedAt) diff --git a/src/internal/operations/backup_test.go b/src/internal/operations/backup_test.go index f867e2a11..149448e66 100644 --- a/src/internal/operations/backup_test.go +++ b/src/internal/operations/backup_test.go @@ -432,258 +432,6 @@ func (suite *BackupOpSuite) TestBackupOperation_PersistResults() { } } -func (suite *BackupOpSuite) TestBackupOperation_VerifyDistinctBases() { - const user = "a-user" - - table := []struct { - name string - input []*kopia.ManifestEntry - errCheck assert.ErrorAssertionFunc - }{ - { - name: "SingleManifestMultipleReasons", - input: []*kopia.ManifestEntry{ - { - Manifest: &snapshot.Manifest{ - ID: "id1", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EventsCategory, - }, - }, - }, - }, - errCheck: assert.NoError, - }, - { - name: "MultipleManifestsDistinctReason", - input: []*kopia.ManifestEntry{ - { - Manifest: &snapshot.Manifest{ - ID: "id1", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - { - Manifest: &snapshot.Manifest{ - ID: "id2", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EventsCategory, - }, - }, - }, - }, - errCheck: assert.NoError, - }, - { - name: "MultipleManifestsSameReason", - input: []*kopia.ManifestEntry{ - { - Manifest: &snapshot.Manifest{ - ID: "id1", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - { - Manifest: &snapshot.Manifest{ - ID: "id2", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - }, - errCheck: assert.Error, - }, - { - name: "MultipleManifestsSameReasonOneIncomplete", - input: []*kopia.ManifestEntry{ - { - Manifest: &snapshot.Manifest{ - ID: "id1", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - { - Manifest: &snapshot.Manifest{ - ID: "id2", - IncompleteReason: "checkpoint", - }, - Reasons: []kopia.Reason{ - { - ResourceOwner: user, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - }, - errCheck: assert.NoError, - }, - } - - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - test.errCheck(t, verifyDistinctBases(test.input)) - }) - } -} - -func (suite *BackupOpSuite) TestBackupOperation_CollectMetadata() { - var ( - tenant = "a-tenant" - resourceOwner = "a-user" - fileNames = []string{ - "delta", - "paths", - } - - emailDeltaPath = makeMetadataPath( - suite.T(), - tenant, - path.ExchangeService, - resourceOwner, - path.EmailCategory, - fileNames[0], - ) - emailPathsPath = makeMetadataPath( - suite.T(), - tenant, - path.ExchangeService, - resourceOwner, - path.EmailCategory, - fileNames[1], - ) - contactsDeltaPath = makeMetadataPath( - suite.T(), - tenant, - path.ExchangeService, - resourceOwner, - path.ContactsCategory, - fileNames[0], - ) - contactsPathsPath = makeMetadataPath( - suite.T(), - tenant, - path.ExchangeService, - resourceOwner, - path.ContactsCategory, - fileNames[1], - ) - ) - - table := []struct { - name string - inputMan *kopia.ManifestEntry - inputFiles []string - expected []path.Path - }{ - { - name: "SingleReasonSingleFile", - inputMan: &kopia.ManifestEntry{ - Manifest: &snapshot.Manifest{}, - Reasons: []kopia.Reason{ - { - ResourceOwner: resourceOwner, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - inputFiles: []string{fileNames[0]}, - expected: []path.Path{emailDeltaPath}, - }, - { - name: "SingleReasonMultipleFiles", - inputMan: &kopia.ManifestEntry{ - Manifest: &snapshot.Manifest{}, - Reasons: []kopia.Reason{ - { - ResourceOwner: resourceOwner, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - }, - }, - inputFiles: fileNames, - expected: []path.Path{emailDeltaPath, emailPathsPath}, - }, - { - name: "MultipleReasonsMultipleFiles", - inputMan: &kopia.ManifestEntry{ - Manifest: &snapshot.Manifest{}, - Reasons: []kopia.Reason{ - { - ResourceOwner: resourceOwner, - Service: path.ExchangeService, - Category: path.EmailCategory, - }, - { - ResourceOwner: resourceOwner, - Service: path.ExchangeService, - Category: path.ContactsCategory, - }, - }, - }, - inputFiles: fileNames, - expected: []path.Path{ - emailDeltaPath, - emailPathsPath, - contactsDeltaPath, - contactsPathsPath, - }, - }, - } - - for _, test := range table { - suite.T().Run(test.name, func(t *testing.T) { - ctx, flush := tester.NewContext() - defer flush() - - mr := &mockRestorer{} - - _, err := collectMetadata(ctx, mr, test.inputMan, test.inputFiles, tenant) - assert.NoError(t, err) - - checkPaths(t, test.expected, mr.gotPaths) - }) - } -} - func (suite *BackupOpSuite) TestBackupOperation_ConsumeBackupDataCollections_Paths() { var ( tenant = "a-tenant" diff --git a/src/internal/operations/manifests.go b/src/internal/operations/manifests.go index fe0e4d09d..9c765fe70 100644 --- a/src/internal/operations/manifests.go +++ b/src/internal/operations/manifests.go @@ -3,7 +3,7 @@ package operations import ( "context" - multierror "github.com/hashicorp/go-multierror" + "github.com/alcionai/clues" "github.com/kopia/kopia/repo/manifest" "github.com/pkg/errors" @@ -12,6 +12,7 @@ import ( "github.com/alcionai/corso/src/internal/kopia" "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/pkg/backup" + "github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/path" ) @@ -44,6 +45,7 @@ func produceManifestsAndMetadata( reasons []kopia.Reason, tenantID string, getMetadata bool, + errs fault.Adder, ) ([]*kopia.ManifestEntry, []data.Collection, bool, error) { var ( metadataFiles = graph.AllMetadataFileNames() @@ -68,12 +70,10 @@ func produceManifestsAndMetadata( // // TODO(ashmrtn): This may need updating if we start sourcing item backup // details from previous snapshots when using kopia-assisted incrementals. - if err := verifyDistinctBases(ms); err != nil { - logger.Ctx(ctx).Warnw( + if err := verifyDistinctBases(ctx, ms, errs); err != nil { + logger.Ctx(ctx).With("error", err).Infow( "base snapshot collision, falling back to full backup", - "error", - err, - ) + clues.Slice(ctx)...) return ms, nil, false, nil } @@ -83,40 +83,41 @@ func produceManifestsAndMetadata( continue } + mctx := clues.Add(ctx, "manifest_id", man.ID) + bID, ok := man.GetTag(kopia.TagBackupID) if !ok { - return nil, nil, false, errors.New("snapshot manifest missing backup ID") + err = clues.New("snapshot manifest missing backup ID").WithMap(clues.Values(mctx)) + return nil, nil, false, err } - dID, _, err := gdi.GetDetailsIDFromBackupID(ctx, model.StableID(bID)) + mctx = clues.Add(mctx, "manifest_backup_id", man.ID) + + dID, _, err := gdi.GetDetailsIDFromBackupID(mctx, model.StableID(bID)) if err != nil { // if no backup exists for any of the complete manifests, we want // to fall back to a complete backup. if errors.Is(err, kopia.ErrNotFound) { - logger.Ctx(ctx).Infow( - "backup missing, falling back to full backup", - "backup_id", bID) - + logger.Ctx(ctx).Infow("backup missing, falling back to full backup", clues.Slice(mctx)...) return ms, nil, false, nil } return nil, nil, false, errors.Wrap(err, "retrieving prior backup data") } + mctx = clues.Add(mctx, "manifest_details_id", dID) + // if no detailsID exists for any of the complete manifests, we want // to fall back to a complete backup. This is a temporary prevention // mechanism to keep backups from falling into a perpetually bad state. // This makes an assumption that the ID points to a populated set of // details; we aren't doing the work to look them up. if len(dID) == 0 { - logger.Ctx(ctx).Infow( - "backup missing details ID, falling back to full backup", - "backup_id", bID) - + logger.Ctx(ctx).Infow("backup missing details ID, falling back to full backup", clues.Slice(mctx)...) return ms, nil, false, nil } - colls, err := collectMetadata(ctx, mr, man, metadataFiles, tenantID) + colls, err := collectMetadata(mctx, mr, man, metadataFiles, tenantID) if err != nil && !errors.Is(err, kopia.ErrNotFound) { // prior metadata isn't guaranteed to exist. // if it doesn't, we'll just have to do a @@ -134,9 +135,9 @@ func produceManifestsAndMetadata( // of manifests, that each manifest's Reason (owner, service, category) is only // included once. If a reason is duplicated by any two manifests, an error is // returned. -func verifyDistinctBases(mans []*kopia.ManifestEntry) error { +func verifyDistinctBases(ctx context.Context, mans []*kopia.ManifestEntry, errs fault.Adder) error { var ( - errs *multierror.Error + failed bool reasons = map[string]manifest.ID{} ) @@ -155,10 +156,11 @@ func verifyDistinctBases(mans []*kopia.ManifestEntry) error { reasonKey := reason.ResourceOwner + reason.Service.String() + reason.Category.String() if b, ok := reasons[reasonKey]; ok { - errs = multierror.Append(errs, errors.Errorf( - "multiple base snapshots source data for %s %s. IDs: %s, %s", - reason.Service, reason.Category, b, man.ID, - )) + failed = true + + errs.Add(clues.New("manifests have overlapping reasons"). + WithMap(clues.Values(ctx)). + With("other_manifest_id", b)) continue } @@ -167,7 +169,11 @@ func verifyDistinctBases(mans []*kopia.ManifestEntry) error { } } - return errs.ErrorOrNil() + if failed { + return clues.New("multiple base snapshots qualify").WithMap(clues.Values(ctx)) + } + + return nil } // collectMetadata retrieves all metadata files associated with the manifest. @@ -191,7 +197,9 @@ func collectMetadata( reason.Category, true) if err != nil { - return nil, errors.Wrapf(err, "building metadata path") + return nil, clues. + Wrap(err, "building metadata path"). + WithAll("metadata_file", fn, "category", reason.Category) } paths = append(paths, p) diff --git a/src/internal/operations/manifests_test.go b/src/internal/operations/manifests_test.go index 7cfc9ac9a..93cdb982f 100644 --- a/src/internal/operations/manifests_test.go +++ b/src/internal/operations/manifests_test.go @@ -14,6 +14,7 @@ import ( "github.com/alcionai/corso/src/internal/model" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/backup" + "github.com/alcionai/corso/src/pkg/fault/mock" "github.com/alcionai/corso/src/pkg/path" ) @@ -400,7 +401,10 @@ func (suite *OperationsManifestsUnitSuite) TestVerifyDistinctBases() { } for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - err := verifyDistinctBases(test.mans) + ctx, flush := tester.NewContext() + defer flush() + + err := verifyDistinctBases(ctx, test.mans, mock.NewAdder()) test.expect(t, err) }) } @@ -646,6 +650,8 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() { ctx, flush := tester.NewContext() defer flush() + ma := mock.NewAdder() + mans, dcs, b, err := produceManifestsAndMetadata( ctx, &test.mr, @@ -653,7 +659,7 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() { test.reasons, tid, test.getMeta, - ) + ma) test.assertErr(t, err) test.assertB(t, b) @@ -683,3 +689,270 @@ func (suite *OperationsManifestsUnitSuite) TestProduceManifestsAndMetadata() { }) } } + +// --------------------------------------------------------------------------- +// older tests +// --------------------------------------------------------------------------- + +type BackupManifestSuite struct { + suite.Suite +} + +func TestBackupManifestSuite(t *testing.T) { + suite.Run(t, new(BackupOpSuite)) +} + +func (suite *BackupManifestSuite) TestBackupOperation_VerifyDistinctBases() { + const user = "a-user" + + table := []struct { + name string + input []*kopia.ManifestEntry + errCheck assert.ErrorAssertionFunc + }{ + { + name: "SingleManifestMultipleReasons", + input: []*kopia.ManifestEntry{ + { + Manifest: &snapshot.Manifest{ + ID: "id1", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EventsCategory, + }, + }, + }, + }, + errCheck: assert.NoError, + }, + { + name: "MultipleManifestsDistinctReason", + input: []*kopia.ManifestEntry{ + { + Manifest: &snapshot.Manifest{ + ID: "id1", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + { + Manifest: &snapshot.Manifest{ + ID: "id2", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EventsCategory, + }, + }, + }, + }, + errCheck: assert.NoError, + }, + { + name: "MultipleManifestsSameReason", + input: []*kopia.ManifestEntry{ + { + Manifest: &snapshot.Manifest{ + ID: "id1", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + { + Manifest: &snapshot.Manifest{ + ID: "id2", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + }, + errCheck: assert.Error, + }, + { + name: "MultipleManifestsSameReasonOneIncomplete", + input: []*kopia.ManifestEntry{ + { + Manifest: &snapshot.Manifest{ + ID: "id1", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + { + Manifest: &snapshot.Manifest{ + ID: "id2", + IncompleteReason: "checkpoint", + }, + Reasons: []kopia.Reason{ + { + ResourceOwner: user, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + }, + errCheck: assert.NoError, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + ctx, flush := tester.NewContext() + defer flush() + + test.errCheck(t, verifyDistinctBases(ctx, test.input, mock.NewAdder())) + }) + } +} + +func (suite *BackupManifestSuite) TestBackupOperation_CollectMetadata() { + var ( + tenant = "a-tenant" + resourceOwner = "a-user" + fileNames = []string{ + "delta", + "paths", + } + + emailDeltaPath = makeMetadataPath( + suite.T(), + tenant, + path.ExchangeService, + resourceOwner, + path.EmailCategory, + fileNames[0], + ) + emailPathsPath = makeMetadataPath( + suite.T(), + tenant, + path.ExchangeService, + resourceOwner, + path.EmailCategory, + fileNames[1], + ) + contactsDeltaPath = makeMetadataPath( + suite.T(), + tenant, + path.ExchangeService, + resourceOwner, + path.ContactsCategory, + fileNames[0], + ) + contactsPathsPath = makeMetadataPath( + suite.T(), + tenant, + path.ExchangeService, + resourceOwner, + path.ContactsCategory, + fileNames[1], + ) + ) + + table := []struct { + name string + inputMan *kopia.ManifestEntry + inputFiles []string + expected []path.Path + }{ + { + name: "SingleReasonSingleFile", + inputMan: &kopia.ManifestEntry{ + Manifest: &snapshot.Manifest{}, + Reasons: []kopia.Reason{ + { + ResourceOwner: resourceOwner, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + inputFiles: []string{fileNames[0]}, + expected: []path.Path{emailDeltaPath}, + }, + { + name: "SingleReasonMultipleFiles", + inputMan: &kopia.ManifestEntry{ + Manifest: &snapshot.Manifest{}, + Reasons: []kopia.Reason{ + { + ResourceOwner: resourceOwner, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + }, + }, + inputFiles: fileNames, + expected: []path.Path{emailDeltaPath, emailPathsPath}, + }, + { + name: "MultipleReasonsMultipleFiles", + inputMan: &kopia.ManifestEntry{ + Manifest: &snapshot.Manifest{}, + Reasons: []kopia.Reason{ + { + ResourceOwner: resourceOwner, + Service: path.ExchangeService, + Category: path.EmailCategory, + }, + { + ResourceOwner: resourceOwner, + Service: path.ExchangeService, + Category: path.ContactsCategory, + }, + }, + }, + inputFiles: fileNames, + expected: []path.Path{ + emailDeltaPath, + emailPathsPath, + contactsDeltaPath, + contactsPathsPath, + }, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + ctx, flush := tester.NewContext() + defer flush() + + mr := &mockRestorer{} + + _, err := collectMetadata(ctx, mr, test.inputMan, test.inputFiles, tenant) + assert.NoError(t, err) + + checkPaths(t, test.expected, mr.gotPaths) + }) + } +} diff --git a/src/pkg/fault/fault.go b/src/pkg/fault/fault.go index f4171ce77..69017029c 100644 --- a/src/pkg/fault/fault.go +++ b/src/pkg/fault/fault.go @@ -96,7 +96,9 @@ func (e *Errors) setErr(err error) *Errors { return e } -// TODO: introduce Adder interface +type Adder interface { + Add(err error) *Errors +} // Add appends the error to the slice of recoverable and // iterated errors (ie: errors.errs). If failFast is true, diff --git a/src/pkg/fault/mock/mock.go b/src/pkg/fault/mock/mock.go new file mode 100644 index 000000000..ba560996d --- /dev/null +++ b/src/pkg/fault/mock/mock.go @@ -0,0 +1,17 @@ +package mock + +import "github.com/alcionai/corso/src/pkg/fault" + +// Adder mocks an adder interface for testing. +type Adder struct { + Errs []error +} + +func NewAdder() *Adder { + return &Adder{Errs: []error{}} +} + +func (ma *Adder) Add(err error) *fault.Errors { + ma.Errs = append(ma.Errs, err) + return fault.New(false) +} From 387f8e8cd7eb2e1f4565d241ebe9472ad1dd0207 Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Tue, 31 Jan 2023 14:48:30 -0800 Subject: [PATCH 18/46] Deserialize OneDrive metadata during backup (#2263) ## Description Create helper functions to deserialize OneDrive metadata during subsequent backups. Currently deserialized data is not passed to the function that generates Collections nor is metadata passed in even though it's wired through GraphConnector Additional changes to BackupOp and operations/manifests.go are required to begin passing in metadata ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * closes #2122 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- src/internal/connector/data_collections.go | 5 +- .../connector/onedrive/collections.go | 140 +++++++- .../connector/onedrive/collections_test.go | 302 +++++++++++++++++- src/internal/connector/onedrive/drive_test.go | 2 +- .../connector/sharepoint/data_collections.go | 4 +- 5 files changed, 444 insertions(+), 9 deletions(-) diff --git a/src/internal/connector/data_collections.go b/src/internal/connector/data_collections.go index 7d187a854..410a05462 100644 --- a/src/internal/connector/data_collections.go +++ b/src/internal/connector/data_collections.go @@ -83,7 +83,7 @@ func (gc *GraphConnector) DataCollections( return colls, excludes, nil case selectors.ServiceOneDrive: - return gc.OneDriveDataCollections(ctx, sels, ctrlOpts) + return gc.OneDriveDataCollections(ctx, sels, metadata, ctrlOpts) case selectors.ServiceSharePoint: colls, excludes, err := sharepoint.DataCollections( @@ -182,6 +182,7 @@ func (fm odFolderMatcher) Matches(dir string) bool { func (gc *GraphConnector) OneDriveDataCollections( ctx context.Context, selector selectors.Selector, + metadata []data.Collection, ctrlOpts control.Options, ) ([]data.Collection, map[string]struct{}, error) { odb, err := selector.ToOneDriveBackup() @@ -209,7 +210,7 @@ func (gc *GraphConnector) OneDriveDataCollections( gc.Service, gc.UpdateStatus, ctrlOpts, - ).Get(ctx) + ).Get(ctx, metadata) if err != nil { return nil, nil, support.WrapAndAppend(user, err, errs) } diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index f83ce342a..35b81f7f2 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -2,7 +2,9 @@ package onedrive import ( "context" + "encoding/json" "fmt" + "io" "net/http" "strings" @@ -92,9 +94,145 @@ func NewCollections( } } +func deserializeMetadata( + ctx context.Context, + cols []data.Collection, +) (map[string]string, map[string]map[string]string, error) { + logger.Ctx(ctx).Infow( + "deserialzing previous backup metadata", + "num_collections", + len(cols), + ) + + prevDeltas := map[string]string{} + prevFolders := map[string]map[string]string{} + + for _, col := range cols { + items := col.Items() + + for breakLoop := false; !breakLoop; { + select { + case <-ctx.Done(): + return nil, nil, errors.Wrap(ctx.Err(), "deserialzing previous backup metadata") + + case item, ok := <-items: + if !ok { + // End of collection items. + breakLoop = true + break + } + + var err error + + switch item.UUID() { + case graph.PreviousPathFileName: + err = deserializeMap(item.ToReader(), prevFolders) + + case graph.DeltaURLsFileName: + err = deserializeMap(item.ToReader(), prevDeltas) + + default: + logger.Ctx(ctx).Infow( + "skipping unknown metadata file", + "file_name", + item.UUID(), + ) + + continue + } + + if err == nil { + // Successful decode. + continue + } + + // This is conservative, but report an error if any of the items for + // any of the deserialized maps have duplicate drive IDs. This will + // cause the entire backup to fail, but it's not clear if higher + // layers would have caught this. Worst case if we don't handle this + // we end up in a situation where we're sourcing items from the wrong + // base in kopia wrapper. + if errors.Is(err, errExistingMapping) { + return nil, nil, errors.Wrapf( + err, + "deserializing metadata file %s", + item.UUID(), + ) + } + + logger.Ctx(ctx).Errorw( + "deserializing base backup metadata. Falling back to full backup for selected drives", + "error", + err, + "file_name", + item.UUID(), + ) + } + } + + // Go through and remove partial results (i.e. path mapping but no delta URL + // or vice-versa). + for k := range prevDeltas { + if _, ok := prevFolders[k]; !ok { + delete(prevDeltas, k) + } + } + + for k := range prevFolders { + if _, ok := prevDeltas[k]; !ok { + delete(prevFolders, k) + } + } + } + + return prevDeltas, prevFolders, nil +} + +var errExistingMapping = errors.New("mapping already exists for same drive ID") + +// deserializeMap takes an reader and a map of already deserialized items and +// adds the newly deserialized items to alreadyFound. Items are only added to +// alreadyFound if none of the keys in the freshly deserialized map already +// exist in alreadyFound. reader is closed at the end of this function. +func deserializeMap[T any](reader io.ReadCloser, alreadyFound map[string]T) error { + defer reader.Close() + + tmp := map[string]T{} + + err := json.NewDecoder(reader).Decode(&tmp) + if err != nil { + return errors.Wrap(err, "deserializing file contents") + } + + var duplicate bool + + for k := range tmp { + if _, ok := alreadyFound[k]; ok { + duplicate = true + break + } + } + + if duplicate { + return errors.WithStack(errExistingMapping) + } + + maps.Copy(alreadyFound, tmp) + + return nil +} + // Retrieves drive data as set of `data.Collections` and a set of item names to // be excluded from the upcoming backup. -func (c *Collections) Get(ctx context.Context) ([]data.Collection, map[string]struct{}, error) { +func (c *Collections) Get( + ctx context.Context, + prevMetadata []data.Collection, +) ([]data.Collection, map[string]struct{}, error) { + _, _, err := deserializeMetadata(ctx, prevMetadata) + if err != nil { + return nil, nil, err + } + // Enumerate drives for the specified resourceOwner pager, err := PagerForSource(c.source, c.service, c.resourceOwner, nil) if err != nil { diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index b69253918..c250afe2a 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -11,8 +11,11 @@ import ( "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/connector/graph" + "github.com/alcionai/corso/src/internal/connector/support" + "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/control" + "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -621,13 +624,304 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { } } -func driveItem(id string, name string, path string, isFile, isFolder, isPackage bool) models.DriveItemable { +func (suite *OneDriveCollectionsSuite) TestDeserializeMetadata() { + tenant := "a-tenant" + user := "a-user" + driveID1 := "1" + driveID2 := "2" + deltaURL1 := "url/1" + deltaURL2 := "url/2" + + folderID1 := "folder1" + folderID2 := "folder2" + path1 := "folder1/path" + path2 := "folder2/path" + + table := []struct { + name string + // Each function returns the set of files for a single data.Collection. + cols []func() []graph.MetadataCollectionEntry + expectedDeltas map[string]string + expectedPaths map[string]map[string]string + errCheck assert.ErrorAssertionFunc + }{ + { + name: "SuccessOneDriveAllOneCollection", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + }, + expectedDeltas: map[string]string{ + driveID1: deltaURL1, + }, + expectedPaths: map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + errCheck: assert.NoError, + }, + { + name: "MissingPaths", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + } + }, + }, + expectedDeltas: map[string]string{}, + expectedPaths: map[string]map[string]string{}, + errCheck: assert.NoError, + }, + { + name: "MissingDeltas", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + }, + expectedDeltas: map[string]string{}, + expectedPaths: map[string]map[string]string{}, + errCheck: assert.NoError, + }, + { + name: "SuccessTwoDrivesTwoCollections", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID2: deltaURL2}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID2: { + folderID2: path2, + }, + }, + ), + } + }, + }, + expectedDeltas: map[string]string{ + driveID1: deltaURL1, + driveID2: deltaURL2, + }, + expectedPaths: map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + driveID2: { + folderID2: path2, + }, + }, + errCheck: assert.NoError, + }, + { + // Bad formats are logged but skip adding entries to the maps and don't + // return an error. + name: "BadFormat", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]string{driveID1: deltaURL1}, + ), + } + }, + }, + expectedDeltas: map[string]string{}, + expectedPaths: map[string]map[string]string{}, + errCheck: assert.NoError, + }, + { + // Unexpected files are logged and skipped. They don't cause an error to + // be returned. + name: "BadFileName", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + graph.NewMetadataEntry( + "foo", + map[string]string{driveID1: deltaURL1}, + ), + } + }, + }, + expectedDeltas: map[string]string{ + driveID1: deltaURL1, + }, + expectedPaths: map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + errCheck: assert.NoError, + }, + { + name: "DriveAlreadyFound_Paths", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID2: path2, + }, + }, + ), + } + }, + }, + expectedDeltas: nil, + expectedPaths: nil, + errCheck: assert.Error, + }, + { + name: "DriveAlreadyFound_Deltas", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL2}, + ), + } + }, + }, + expectedDeltas: nil, + expectedPaths: nil, + errCheck: assert.Error, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + ctx, flush := tester.NewContext() + defer flush() + + cols := []data.Collection{} + + for _, c := range test.cols { + mc, err := graph.MakeMetadataCollection( + tenant, + user, + path.OneDriveService, + path.FilesCategory, + c(), + func(*support.ConnectorOperationStatus) {}, + ) + require.NoError(t, err) + + cols = append(cols, mc) + } + + deltas, paths, err := deserializeMetadata(ctx, cols) + test.errCheck(t, err) + + assert.Equal(t, test.expectedDeltas, deltas) + assert.Equal(t, test.expectedPaths, paths) + }) + } +} + +func driveItem(id string, name string, parentPath string, isFile, isFolder, isPackage bool) models.DriveItemable { item := models.NewDriveItem() item.SetName(&name) item.SetId(&id) parentReference := models.NewItemReference() - parentReference.SetPath(&path) + parentReference.SetPath(&parentPath) item.SetParentReference(parentReference) switch { @@ -644,13 +938,13 @@ func driveItem(id string, name string, path string, isFile, isFolder, isPackage // delItem creates a DriveItemable that is marked as deleted. path must be set // to the base drive path. -func delItem(id string, path string, isFile, isFolder, isPackage bool) models.DriveItemable { +func delItem(id string, parentPath string, isFile, isFolder, isPackage bool) models.DriveItemable { item := models.NewDriveItem() item.SetId(&id) item.SetDeleted(models.NewDeleted()) parentReference := models.NewItemReference() - parentReference.SetPath(&path) + parentReference.SetPath(&parentPath) item.SetParentReference(parentReference) switch { diff --git a/src/internal/connector/onedrive/drive_test.go b/src/internal/connector/onedrive/drive_test.go index 36fef30ab..0ba3ec1c2 100644 --- a/src/internal/connector/onedrive/drive_test.go +++ b/src/internal/connector/onedrive/drive_test.go @@ -463,7 +463,7 @@ func (suite *OneDriveSuite) TestOneDriveNewCollections() { service, service.updateStatus, control.Options{}, - ).Get(ctx) + ).Get(ctx, nil) assert.NoError(t, err) // Don't expect excludes as this isn't an incremental backup. assert.Empty(t, excludes) diff --git a/src/internal/connector/sharepoint/data_collections.go b/src/internal/connector/sharepoint/data_collections.go index 6011c32a0..1fd2f786d 100644 --- a/src/internal/connector/sharepoint/data_collections.go +++ b/src/internal/connector/sharepoint/data_collections.go @@ -152,7 +152,9 @@ func collectLibraries( updater.UpdateStatus, ctrlOpts) - odcs, excludes, err := colls.Get(ctx) + // TODO(ashmrtn): Pass previous backup metadata when SharePoint supports delta + // token-based incrementals. + odcs, excludes, err := colls.Get(ctx, nil) if err != nil { return nil, nil, support.WrapAndAppend(siteID, err, errs) } From c510af3fda60202f7379b4da06c2bf2b36f4b2eb Mon Sep 17 00:00:00 2001 From: Vaibhav Kamra Date: Tue, 31 Jan 2023 17:01:31 -0800 Subject: [PATCH 19/46] Bump up max page size preference for exchange delta requests (#2338) ## Description Reduces the number of roundtrips when requesting delta records for exchange TODO: Need to investigate OneDrive behavior - this header doesn't appear to work with OneDrive. ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [ ] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2332 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [x] :green_heart: E2E --- CHANGELOG.md | 1 + .../connector/exchange/api/options.go | 22 +++++++++++++++++++ src/internal/connector/exchange/api/shared.go | 3 +++ 3 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b96aca9e5..3333c7ad6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Document Corso's fault-tolerance and restartability features - Add retries on timeouts and status code 500 for Exchange +- Increase page size preference for delta requests for Exchange to reduce number of roundtrips ## [v0.2.0] (alpha) - 2023-1-29 diff --git a/src/internal/connector/exchange/api/options.go b/src/internal/connector/exchange/api/options.go index 49debf334..67725225f 100644 --- a/src/internal/connector/exchange/api/options.go +++ b/src/internal/connector/exchange/api/options.go @@ -3,6 +3,7 @@ package api import ( "fmt" + abstractions "github.com/microsoft/kiota-abstractions-go" "github.com/microsoftgraph/msgraph-sdk-go/users" ) @@ -53,6 +54,16 @@ var ( } ) +const ( + // headerKeyPrefer is used to set query preferences + headerKeyPrefer = "Prefer" + // maxPageSizeHeaderFmt is used to indicate max page size + // preferences + maxPageSizeHeaderFmt = "odata.maxpagesize=%d" + // deltaMaxPageSize is the max page size to use for delta queries + deltaMaxPageSize = 200 +) + // ----------------------------------------------------------------------- // exchange.Query Option Section // These functions can be used to filter a response on M365 @@ -71,8 +82,10 @@ func optionsForFolderMessagesDelta( requestParameters := &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetQueryParameters{ Select: selecting, } + options := &users.ItemMailFoldersItemMessagesDeltaRequestBuilderGetRequestConfiguration{ QueryParameters: requestParameters, + Headers: buildDeltaRequestHeaders(), } return options, nil @@ -218,6 +231,7 @@ func optionsForContactFoldersItemDelta( options := &users.ItemContactFoldersItemContactsDeltaRequestBuilderGetRequestConfiguration{ QueryParameters: requestParameters, + Headers: buildDeltaRequestHeaders(), } return options, nil @@ -275,3 +289,11 @@ func buildOptions(fields []string, allowed map[string]struct{}) ([]string, error return append(returnedOptions, fields...), nil } + +// buildDeltaRequestHeaders returns the headers we add to delta page requests +func buildDeltaRequestHeaders() *abstractions.RequestHeaders { + headers := abstractions.NewRequestHeaders() + headers.Add(headerKeyPrefer, fmt.Sprintf(maxPageSizeHeaderFmt, deltaMaxPageSize)) + + return headers +} diff --git a/src/internal/connector/exchange/api/shared.go b/src/internal/connector/exchange/api/shared.go index d89ce7411..9cb4d8ab0 100644 --- a/src/internal/connector/exchange/api/shared.go +++ b/src/internal/connector/exchange/api/shared.go @@ -8,6 +8,7 @@ import ( "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" + "github.com/alcionai/corso/src/pkg/logger" ) // --------------------------------------------------------------------------- @@ -82,6 +83,8 @@ func getItemsAddedAndRemovedFromContainer( return nil, nil, "", err } + logger.Ctx(ctx).Infow("Got page", "items", len(items)) + // iterate through the items in the page for _, item := range items { // if the additional data conains a `@removed` key, the value will either From b5b457639338369e5e165788b2781b7e3493bd07 Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Tue, 31 Jan 2023 17:25:27 -0800 Subject: [PATCH 20/46] Run gofmt on beta package (#2340) ## Description This package is mostly generated code, but since it's not formatted and check in it causes subsequent runs of gofmt to format it and add chaff to git diff. Checking in the formatted version removes the chaff. ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [x] :broom: Tech Debt/Cleanup --- .../models/horizontal_section_layout_type.go | 76 ++--- .../models/meta_data_key_string_pair.go | 195 ++++++------ .../models/meta_data_key_string_pairable.go | 20 +- .../graph/betasdk/models/page_layout_type.go | 52 ++-- .../betasdk/models/page_promotion_type.go | 52 ++-- .../graph/betasdk/models/publication_facet.go | 195 ++++++------ .../betasdk/models/publication_facetable.go | 20 +- .../graph/betasdk/models/reactions_facet.go | 241 ++++++++------- .../betasdk/models/reactions_facetable.go | 24 +- .../betasdk/models/section_emphasis_type.go | 58 ++-- .../graph/betasdk/models/site_access_type.go | 46 +-- .../betasdk/models/site_security_level.go | 76 ++--- .../graph/betasdk/models/site_settings.go | 195 ++++++------ .../graph/betasdk/models/site_settingsable.go | 20 +- .../graph/betasdk/models/standard_web_part.go | 138 +++++---- .../betasdk/models/standard_web_partable.go | 16 +- .../graph/betasdk/models/text_web_part.go | 92 +++--- .../graph/betasdk/models/text_web_partable.go | 12 +- .../betasdk/models/title_area_layout_type.go | 58 ++-- .../models/title_area_text_alignment_type.go | 46 +-- .../graph/betasdk/models/web_part_position.go | 287 +++++++++--------- .../betasdk/models/web_part_positionable.go | 28 +- 22 files changed, 1019 insertions(+), 928 deletions(-) diff --git a/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go b/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go index 43c2643a2..80e208ffe 100644 --- a/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go +++ b/src/internal/connector/graph/betasdk/models/horizontal_section_layout_type.go @@ -1,52 +1,54 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type HorizontalSectionLayoutType int const ( - NONE_HORIZONTALSECTIONLAYOUTTYPE HorizontalSectionLayoutType = iota - ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE - TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE - THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE - ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE - ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE - FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE - UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE + NONE_HORIZONTALSECTIONLAYOUTTYPE HorizontalSectionLayoutType = iota + ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE + TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE + UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE ) func (i HorizontalSectionLayoutType) String() string { - return []string{"none", "oneColumn", "twoColumns", "threeColumns", "oneThirdLeftColumn", "oneThirdRightColumn", "fullWidth", "unknownFutureValue"}[i] + return []string{"none", "oneColumn", "twoColumns", "threeColumns", "oneThirdLeftColumn", "oneThirdRightColumn", "fullWidth", "unknownFutureValue"}[i] } func ParseHorizontalSectionLayoutType(v string) (interface{}, error) { - result := NONE_HORIZONTALSECTIONLAYOUTTYPE - switch v { - case "none": - result = NONE_HORIZONTALSECTIONLAYOUTTYPE - case "oneColumn": - result = ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE - case "twoColumns": - result = TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE - case "threeColumns": - result = THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE - case "oneThirdLeftColumn": - result = ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE - case "oneThirdRightColumn": - result = ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE - case "fullWidth": - result = FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE - default: - return 0, errors.New("Unknown HorizontalSectionLayoutType value: " + v) - } - return &result, nil + result := NONE_HORIZONTALSECTIONLAYOUTTYPE + switch v { + case "none": + result = NONE_HORIZONTALSECTIONLAYOUTTYPE + case "oneColumn": + result = ONECOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "twoColumns": + result = TWOCOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + case "threeColumns": + result = THREECOLUMNS_HORIZONTALSECTIONLAYOUTTYPE + case "oneThirdLeftColumn": + result = ONETHIRDLEFTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "oneThirdRightColumn": + result = ONETHIRDRIGHTCOLUMN_HORIZONTALSECTIONLAYOUTTYPE + case "fullWidth": + result = FULLWIDTH_HORIZONTALSECTIONLAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_HORIZONTALSECTIONLAYOUTTYPE + default: + return 0, errors.New("Unknown HorizontalSectionLayoutType value: " + v) + } + return &result, nil } func SerializeHorizontalSectionLayoutType(values []HorizontalSectionLayoutType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go index e7df06165..c79f17cfb 100644 --- a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pair.go @@ -1,123 +1,134 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// MetaDataKeyStringPair +// MetaDataKeyStringPair type MetaDataKeyStringPair struct { - // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. - additionalData map[string]interface{} - // Key of the meta data. - key *string - // The OdataType property - odataType *string - // Value of the meta data. - value *string + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Key of the meta data. + key *string + // The OdataType property + odataType *string + // Value of the meta data. + value *string } + // NewMetaDataKeyStringPair instantiates a new metaDataKeyStringPair and sets the default values. -func NewMetaDataKeyStringPair()(*MetaDataKeyStringPair) { - m := &MetaDataKeyStringPair{ - } - m.SetAdditionalData(make(map[string]interface{})); - return m +func NewMetaDataKeyStringPair() *MetaDataKeyStringPair { + m := &MetaDataKeyStringPair{} + m.SetAdditionalData(make(map[string]interface{})) + return m } + // CreateMetaDataKeyStringPairFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateMetaDataKeyStringPairFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewMetaDataKeyStringPair(), nil +func CreateMetaDataKeyStringPairFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewMetaDataKeyStringPair(), nil } + // GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *MetaDataKeyStringPair) GetAdditionalData()(map[string]interface{}) { - return m.additionalData +func (m *MetaDataKeyStringPair) GetAdditionalData() map[string]interface{} { + return m.additionalData } + // GetFieldDeserializers the deserialization information for the current model -func (m *MetaDataKeyStringPair) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) - res["key"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetKey(val) - } - return nil - } - res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetOdataType(val) - } - return nil - } - res["value"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetValue(val) - } - return nil - } - return res +func (m *MetaDataKeyStringPair) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["key"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetKey(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["value"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetValue(val) + } + return nil + } + return res } + // GetKey gets the key property value. Key of the meta data. -func (m *MetaDataKeyStringPair) GetKey()(*string) { - return m.key +func (m *MetaDataKeyStringPair) GetKey() *string { + return m.key } + // GetOdataType gets the @odata.type property value. The OdataType property -func (m *MetaDataKeyStringPair) GetOdataType()(*string) { - return m.odataType +func (m *MetaDataKeyStringPair) GetOdataType() *string { + return m.odataType } + // GetValue gets the value property value. Value of the meta data. -func (m *MetaDataKeyStringPair) GetValue()(*string) { - return m.value +func (m *MetaDataKeyStringPair) GetValue() *string { + return m.value } + // Serialize serializes information the current object -func (m *MetaDataKeyStringPair) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - { - err := writer.WriteStringValue("key", m.GetKey()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("@odata.type", m.GetOdataType()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("value", m.GetValue()) - if err != nil { - return err - } - } - { - err := writer.WriteAdditionalData(m.GetAdditionalData()) - if err != nil { - return err - } - } - return nil +func (m *MetaDataKeyStringPair) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteStringValue("key", m.GetKey()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("value", m.GetValue()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil } + // SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *MetaDataKeyStringPair) SetAdditionalData(value map[string]interface{})() { - m.additionalData = value +func (m *MetaDataKeyStringPair) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value } + // SetKey sets the key property value. Key of the meta data. -func (m *MetaDataKeyStringPair) SetKey(value *string)() { - m.key = value +func (m *MetaDataKeyStringPair) SetKey(value *string) { + m.key = value } + // SetOdataType sets the @odata.type property value. The OdataType property -func (m *MetaDataKeyStringPair) SetOdataType(value *string)() { - m.odataType = value +func (m *MetaDataKeyStringPair) SetOdataType(value *string) { + m.odataType = value } + // SetValue sets the value property value. Value of the meta data. -func (m *MetaDataKeyStringPair) SetValue(value *string)() { - m.value = value +func (m *MetaDataKeyStringPair) SetValue(value *string) { + m.value = value } diff --git a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go index 49908469e..4168f4dce 100644 --- a/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go +++ b/src/internal/connector/graph/betasdk/models/meta_data_key_string_pairable.go @@ -1,17 +1,17 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// MetaDataKeyStringPairable +// MetaDataKeyStringPairable type MetaDataKeyStringPairable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - GetKey()(*string) - GetOdataType()(*string) - GetValue()(*string) - SetKey(value *string)() - SetOdataType(value *string)() - SetValue(value *string)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetKey() *string + GetOdataType() *string + GetValue() *string + SetKey(value *string) + SetOdataType(value *string) + SetValue(value *string) } diff --git a/src/internal/connector/graph/betasdk/models/page_layout_type.go b/src/internal/connector/graph/betasdk/models/page_layout_type.go index 0338a5c30..fce795760 100644 --- a/src/internal/connector/graph/betasdk/models/page_layout_type.go +++ b/src/internal/connector/graph/betasdk/models/page_layout_type.go @@ -1,40 +1,42 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type PageLayoutType int const ( - MICROSOFTRESERVED_PAGELAYOUTTYPE PageLayoutType = iota - ARTICLE_PAGELAYOUTTYPE - HOME_PAGELAYOUTTYPE - UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE + MICROSOFTRESERVED_PAGELAYOUTTYPE PageLayoutType = iota + ARTICLE_PAGELAYOUTTYPE + HOME_PAGELAYOUTTYPE + UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE ) func (i PageLayoutType) String() string { - return []string{"microsoftReserved", "article", "home", "unknownFutureValue"}[i] + return []string{"microsoftReserved", "article", "home", "unknownFutureValue"}[i] } func ParsePageLayoutType(v string) (interface{}, error) { - result := MICROSOFTRESERVED_PAGELAYOUTTYPE - switch v { - case "microsoftReserved": - result = MICROSOFTRESERVED_PAGELAYOUTTYPE - case "article": - result = ARTICLE_PAGELAYOUTTYPE - case "home": - result = HOME_PAGELAYOUTTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE - default: - return 0, errors.New("Unknown PageLayoutType value: " + v) - } - return &result, nil + result := MICROSOFTRESERVED_PAGELAYOUTTYPE + switch v { + case "microsoftReserved": + result = MICROSOFTRESERVED_PAGELAYOUTTYPE + case "article": + result = ARTICLE_PAGELAYOUTTYPE + case "home": + result = HOME_PAGELAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_PAGELAYOUTTYPE + default: + return 0, errors.New("Unknown PageLayoutType value: " + v) + } + return &result, nil } func SerializePageLayoutType(values []PageLayoutType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/page_promotion_type.go b/src/internal/connector/graph/betasdk/models/page_promotion_type.go index a8cbcd058..e78ce63f0 100644 --- a/src/internal/connector/graph/betasdk/models/page_promotion_type.go +++ b/src/internal/connector/graph/betasdk/models/page_promotion_type.go @@ -1,40 +1,42 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type PagePromotionType int const ( - MICROSOFTRESERVED_PAGEPROMOTIONTYPE PagePromotionType = iota - PAGE_PAGEPROMOTIONTYPE - NEWSPOST_PAGEPROMOTIONTYPE - UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE + MICROSOFTRESERVED_PAGEPROMOTIONTYPE PagePromotionType = iota + PAGE_PAGEPROMOTIONTYPE + NEWSPOST_PAGEPROMOTIONTYPE + UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE ) func (i PagePromotionType) String() string { - return []string{"microsoftReserved", "page", "newsPost", "unknownFutureValue"}[i] + return []string{"microsoftReserved", "page", "newsPost", "unknownFutureValue"}[i] } func ParsePagePromotionType(v string) (interface{}, error) { - result := MICROSOFTRESERVED_PAGEPROMOTIONTYPE - switch v { - case "microsoftReserved": - result = MICROSOFTRESERVED_PAGEPROMOTIONTYPE - case "page": - result = PAGE_PAGEPROMOTIONTYPE - case "newsPost": - result = NEWSPOST_PAGEPROMOTIONTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE - default: - return 0, errors.New("Unknown PagePromotionType value: " + v) - } - return &result, nil + result := MICROSOFTRESERVED_PAGEPROMOTIONTYPE + switch v { + case "microsoftReserved": + result = MICROSOFTRESERVED_PAGEPROMOTIONTYPE + case "page": + result = PAGE_PAGEPROMOTIONTYPE + case "newsPost": + result = NEWSPOST_PAGEPROMOTIONTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_PAGEPROMOTIONTYPE + default: + return 0, errors.New("Unknown PagePromotionType value: " + v) + } + return &result, nil } func SerializePagePromotionType(values []PagePromotionType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/publication_facet.go b/src/internal/connector/graph/betasdk/models/publication_facet.go index 87e59d34b..860b88bf3 100644 --- a/src/internal/connector/graph/betasdk/models/publication_facet.go +++ b/src/internal/connector/graph/betasdk/models/publication_facet.go @@ -1,123 +1,134 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// PublicationFacet +// PublicationFacet type PublicationFacet struct { - // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. - additionalData map[string]interface{} - // The state of publication for this document. Either published or checkout. Read-only. - level *string - // The OdataType property - odataType *string - // The unique identifier for the version that is visible to the current caller. Read-only. - versionId *string + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // The state of publication for this document. Either published or checkout. Read-only. + level *string + // The OdataType property + odataType *string + // The unique identifier for the version that is visible to the current caller. Read-only. + versionId *string } + // NewPublicationFacet instantiates a new publicationFacet and sets the default values. -func NewPublicationFacet()(*PublicationFacet) { - m := &PublicationFacet{ - } - m.SetAdditionalData(make(map[string]interface{})); - return m +func NewPublicationFacet() *PublicationFacet { + m := &PublicationFacet{} + m.SetAdditionalData(make(map[string]interface{})) + return m } + // CreatePublicationFacetFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreatePublicationFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewPublicationFacet(), nil +func CreatePublicationFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewPublicationFacet(), nil } + // GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *PublicationFacet) GetAdditionalData()(map[string]interface{}) { - return m.additionalData +func (m *PublicationFacet) GetAdditionalData() map[string]interface{} { + return m.additionalData } + // GetFieldDeserializers the deserialization information for the current model -func (m *PublicationFacet) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) - res["level"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetLevel(val) - } - return nil - } - res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetOdataType(val) - } - return nil - } - res["versionId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetVersionId(val) - } - return nil - } - return res +func (m *PublicationFacet) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["level"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetLevel(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["versionId"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetVersionId(val) + } + return nil + } + return res } + // GetLevel gets the level property value. The state of publication for this document. Either published or checkout. Read-only. -func (m *PublicationFacet) GetLevel()(*string) { - return m.level +func (m *PublicationFacet) GetLevel() *string { + return m.level } + // GetOdataType gets the @odata.type property value. The OdataType property -func (m *PublicationFacet) GetOdataType()(*string) { - return m.odataType +func (m *PublicationFacet) GetOdataType() *string { + return m.odataType } + // GetVersionId gets the versionId property value. The unique identifier for the version that is visible to the current caller. Read-only. -func (m *PublicationFacet) GetVersionId()(*string) { - return m.versionId +func (m *PublicationFacet) GetVersionId() *string { + return m.versionId } + // Serialize serializes information the current object -func (m *PublicationFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - { - err := writer.WriteStringValue("level", m.GetLevel()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("@odata.type", m.GetOdataType()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("versionId", m.GetVersionId()) - if err != nil { - return err - } - } - { - err := writer.WriteAdditionalData(m.GetAdditionalData()) - if err != nil { - return err - } - } - return nil +func (m *PublicationFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteStringValue("level", m.GetLevel()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("versionId", m.GetVersionId()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil } + // SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *PublicationFacet) SetAdditionalData(value map[string]interface{})() { - m.additionalData = value +func (m *PublicationFacet) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value } + // SetLevel sets the level property value. The state of publication for this document. Either published or checkout. Read-only. -func (m *PublicationFacet) SetLevel(value *string)() { - m.level = value +func (m *PublicationFacet) SetLevel(value *string) { + m.level = value } + // SetOdataType sets the @odata.type property value. The OdataType property -func (m *PublicationFacet) SetOdataType(value *string)() { - m.odataType = value +func (m *PublicationFacet) SetOdataType(value *string) { + m.odataType = value } + // SetVersionId sets the versionId property value. The unique identifier for the version that is visible to the current caller. Read-only. -func (m *PublicationFacet) SetVersionId(value *string)() { - m.versionId = value +func (m *PublicationFacet) SetVersionId(value *string) { + m.versionId = value } diff --git a/src/internal/connector/graph/betasdk/models/publication_facetable.go b/src/internal/connector/graph/betasdk/models/publication_facetable.go index 20d82ccf8..4098c89b1 100644 --- a/src/internal/connector/graph/betasdk/models/publication_facetable.go +++ b/src/internal/connector/graph/betasdk/models/publication_facetable.go @@ -1,17 +1,17 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// PublicationFacetable +// PublicationFacetable type PublicationFacetable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - GetLevel()(*string) - GetOdataType()(*string) - GetVersionId()(*string) - SetLevel(value *string)() - SetOdataType(value *string)() - SetVersionId(value *string)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetLevel() *string + GetOdataType() *string + GetVersionId() *string + SetLevel(value *string) + SetOdataType(value *string) + SetVersionId(value *string) } diff --git a/src/internal/connector/graph/betasdk/models/reactions_facet.go b/src/internal/connector/graph/betasdk/models/reactions_facet.go index b298a9fe1..c971925dc 100644 --- a/src/internal/connector/graph/betasdk/models/reactions_facet.go +++ b/src/internal/connector/graph/betasdk/models/reactions_facet.go @@ -1,149 +1,162 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// ReactionsFacet +// ReactionsFacet type ReactionsFacet struct { - // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. - additionalData map[string]interface{} - // Count of comments. - commentCount *int32 - // Count of likes. - likeCount *int32 - // The OdataType property - odataType *string - // Count of shares. - shareCount *int32 + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Count of comments. + commentCount *int32 + // Count of likes. + likeCount *int32 + // The OdataType property + odataType *string + // Count of shares. + shareCount *int32 } + // NewReactionsFacet instantiates a new reactionsFacet and sets the default values. -func NewReactionsFacet()(*ReactionsFacet) { - m := &ReactionsFacet{ - } - m.SetAdditionalData(make(map[string]interface{})); - return m +func NewReactionsFacet() *ReactionsFacet { + m := &ReactionsFacet{} + m.SetAdditionalData(make(map[string]interface{})) + return m } + // CreateReactionsFacetFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateReactionsFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewReactionsFacet(), nil +func CreateReactionsFacetFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewReactionsFacet(), nil } + // GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *ReactionsFacet) GetAdditionalData()(map[string]interface{}) { - return m.additionalData +func (m *ReactionsFacet) GetAdditionalData() map[string]interface{} { + return m.additionalData } + // GetCommentCount gets the commentCount property value. Count of comments. -func (m *ReactionsFacet) GetCommentCount()(*int32) { - return m.commentCount +func (m *ReactionsFacet) GetCommentCount() *int32 { + return m.commentCount } + // GetFieldDeserializers the deserialization information for the current model -func (m *ReactionsFacet) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) - res["commentCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetInt32Value() - if err != nil { - return err - } - if val != nil { - m.SetCommentCount(val) - } - return nil - } - res["likeCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetInt32Value() - if err != nil { - return err - } - if val != nil { - m.SetLikeCount(val) - } - return nil - } - res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetOdataType(val) - } - return nil - } - res["shareCount"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetInt32Value() - if err != nil { - return err - } - if val != nil { - m.SetShareCount(val) - } - return nil - } - return res +func (m *ReactionsFacet) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["commentCount"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetCommentCount(val) + } + return nil + } + res["likeCount"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetLikeCount(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["shareCount"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetInt32Value() + if err != nil { + return err + } + if val != nil { + m.SetShareCount(val) + } + return nil + } + return res } + // GetLikeCount gets the likeCount property value. Count of likes. -func (m *ReactionsFacet) GetLikeCount()(*int32) { - return m.likeCount +func (m *ReactionsFacet) GetLikeCount() *int32 { + return m.likeCount } + // GetOdataType gets the @odata.type property value. The OdataType property -func (m *ReactionsFacet) GetOdataType()(*string) { - return m.odataType +func (m *ReactionsFacet) GetOdataType() *string { + return m.odataType } + // GetShareCount gets the shareCount property value. Count of shares. -func (m *ReactionsFacet) GetShareCount()(*int32) { - return m.shareCount +func (m *ReactionsFacet) GetShareCount() *int32 { + return m.shareCount } + // Serialize serializes information the current object -func (m *ReactionsFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - { - err := writer.WriteInt32Value("commentCount", m.GetCommentCount()) - if err != nil { - return err - } - } - { - err := writer.WriteInt32Value("likeCount", m.GetLikeCount()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("@odata.type", m.GetOdataType()) - if err != nil { - return err - } - } - { - err := writer.WriteInt32Value("shareCount", m.GetShareCount()) - if err != nil { - return err - } - } - { - err := writer.WriteAdditionalData(m.GetAdditionalData()) - if err != nil { - return err - } - } - return nil +func (m *ReactionsFacet) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteInt32Value("commentCount", m.GetCommentCount()) + if err != nil { + return err + } + } + { + err := writer.WriteInt32Value("likeCount", m.GetLikeCount()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteInt32Value("shareCount", m.GetShareCount()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil } + // SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *ReactionsFacet) SetAdditionalData(value map[string]interface{})() { - m.additionalData = value +func (m *ReactionsFacet) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value } + // SetCommentCount sets the commentCount property value. Count of comments. -func (m *ReactionsFacet) SetCommentCount(value *int32)() { - m.commentCount = value +func (m *ReactionsFacet) SetCommentCount(value *int32) { + m.commentCount = value } + // SetLikeCount sets the likeCount property value. Count of likes. -func (m *ReactionsFacet) SetLikeCount(value *int32)() { - m.likeCount = value +func (m *ReactionsFacet) SetLikeCount(value *int32) { + m.likeCount = value } + // SetOdataType sets the @odata.type property value. The OdataType property -func (m *ReactionsFacet) SetOdataType(value *string)() { - m.odataType = value +func (m *ReactionsFacet) SetOdataType(value *string) { + m.odataType = value } + // SetShareCount sets the shareCount property value. Count of shares. -func (m *ReactionsFacet) SetShareCount(value *int32)() { - m.shareCount = value +func (m *ReactionsFacet) SetShareCount(value *int32) { + m.shareCount = value } diff --git a/src/internal/connector/graph/betasdk/models/reactions_facetable.go b/src/internal/connector/graph/betasdk/models/reactions_facetable.go index 4e5086047..acdefec37 100644 --- a/src/internal/connector/graph/betasdk/models/reactions_facetable.go +++ b/src/internal/connector/graph/betasdk/models/reactions_facetable.go @@ -1,19 +1,19 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// ReactionsFacetable +// ReactionsFacetable type ReactionsFacetable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - GetCommentCount()(*int32) - GetLikeCount()(*int32) - GetOdataType()(*string) - GetShareCount()(*int32) - SetCommentCount(value *int32)() - SetLikeCount(value *int32)() - SetOdataType(value *string)() - SetShareCount(value *int32)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetCommentCount() *int32 + GetLikeCount() *int32 + GetOdataType() *string + GetShareCount() *int32 + SetCommentCount(value *int32) + SetLikeCount(value *int32) + SetOdataType(value *string) + SetShareCount(value *int32) } diff --git a/src/internal/connector/graph/betasdk/models/section_emphasis_type.go b/src/internal/connector/graph/betasdk/models/section_emphasis_type.go index 0016aec10..301ae839f 100644 --- a/src/internal/connector/graph/betasdk/models/section_emphasis_type.go +++ b/src/internal/connector/graph/betasdk/models/section_emphasis_type.go @@ -1,43 +1,45 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type SectionEmphasisType int const ( - NONE_SECTIONEMPHASISTYPE SectionEmphasisType = iota - NEUTRAL_SECTIONEMPHASISTYPE - SOFT_SECTIONEMPHASISTYPE - STRONG_SECTIONEMPHASISTYPE - UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE + NONE_SECTIONEMPHASISTYPE SectionEmphasisType = iota + NEUTRAL_SECTIONEMPHASISTYPE + SOFT_SECTIONEMPHASISTYPE + STRONG_SECTIONEMPHASISTYPE + UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE ) func (i SectionEmphasisType) String() string { - return []string{"none", "neutral", "soft", "strong", "unknownFutureValue"}[i] + return []string{"none", "neutral", "soft", "strong", "unknownFutureValue"}[i] } func ParseSectionEmphasisType(v string) (interface{}, error) { - result := NONE_SECTIONEMPHASISTYPE - switch v { - case "none": - result = NONE_SECTIONEMPHASISTYPE - case "neutral": - result = NEUTRAL_SECTIONEMPHASISTYPE - case "soft": - result = SOFT_SECTIONEMPHASISTYPE - case "strong": - result = STRONG_SECTIONEMPHASISTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE - default: - return 0, errors.New("Unknown SectionEmphasisType value: " + v) - } - return &result, nil + result := NONE_SECTIONEMPHASISTYPE + switch v { + case "none": + result = NONE_SECTIONEMPHASISTYPE + case "neutral": + result = NEUTRAL_SECTIONEMPHASISTYPE + case "soft": + result = SOFT_SECTIONEMPHASISTYPE + case "strong": + result = STRONG_SECTIONEMPHASISTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_SECTIONEMPHASISTYPE + default: + return 0, errors.New("Unknown SectionEmphasisType value: " + v) + } + return &result, nil } func SerializeSectionEmphasisType(values []SectionEmphasisType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/site_access_type.go b/src/internal/connector/graph/betasdk/models/site_access_type.go index 052a2efdb..2d4cedffe 100644 --- a/src/internal/connector/graph/betasdk/models/site_access_type.go +++ b/src/internal/connector/graph/betasdk/models/site_access_type.go @@ -1,37 +1,39 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type SiteAccessType int const ( - BLOCK_SITEACCESSTYPE SiteAccessType = iota - FULL_SITEACCESSTYPE - LIMITED_SITEACCESSTYPE + BLOCK_SITEACCESSTYPE SiteAccessType = iota + FULL_SITEACCESSTYPE + LIMITED_SITEACCESSTYPE ) func (i SiteAccessType) String() string { - return []string{"block", "full", "limited"}[i] + return []string{"block", "full", "limited"}[i] } func ParseSiteAccessType(v string) (interface{}, error) { - result := BLOCK_SITEACCESSTYPE - switch v { - case "block": - result = BLOCK_SITEACCESSTYPE - case "full": - result = FULL_SITEACCESSTYPE - case "limited": - result = LIMITED_SITEACCESSTYPE - default: - return 0, errors.New("Unknown SiteAccessType value: " + v) - } - return &result, nil + result := BLOCK_SITEACCESSTYPE + switch v { + case "block": + result = BLOCK_SITEACCESSTYPE + case "full": + result = FULL_SITEACCESSTYPE + case "limited": + result = LIMITED_SITEACCESSTYPE + default: + return 0, errors.New("Unknown SiteAccessType value: " + v) + } + return &result, nil } func SerializeSiteAccessType(values []SiteAccessType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/site_security_level.go b/src/internal/connector/graph/betasdk/models/site_security_level.go index d2733ce47..0c75c164e 100644 --- a/src/internal/connector/graph/betasdk/models/site_security_level.go +++ b/src/internal/connector/graph/betasdk/models/site_security_level.go @@ -1,52 +1,54 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the add method. type SiteSecurityLevel int const ( - // User Defined, default value, no intent. - USERDEFINED_SITESECURITYLEVEL SiteSecurityLevel = iota - // Low. - LOW_SITESECURITYLEVEL - // Medium-low. - MEDIUMLOW_SITESECURITYLEVEL - // Medium. - MEDIUM_SITESECURITYLEVEL - // Medium-high. - MEDIUMHIGH_SITESECURITYLEVEL - // High. - HIGH_SITESECURITYLEVEL + // User Defined, default value, no intent. + USERDEFINED_SITESECURITYLEVEL SiteSecurityLevel = iota + // Low. + LOW_SITESECURITYLEVEL + // Medium-low. + MEDIUMLOW_SITESECURITYLEVEL + // Medium. + MEDIUM_SITESECURITYLEVEL + // Medium-high. + MEDIUMHIGH_SITESECURITYLEVEL + // High. + HIGH_SITESECURITYLEVEL ) func (i SiteSecurityLevel) String() string { - return []string{"userDefined", "low", "mediumLow", "medium", "mediumHigh", "high"}[i] + return []string{"userDefined", "low", "mediumLow", "medium", "mediumHigh", "high"}[i] } func ParseSiteSecurityLevel(v string) (interface{}, error) { - result := USERDEFINED_SITESECURITYLEVEL - switch v { - case "userDefined": - result = USERDEFINED_SITESECURITYLEVEL - case "low": - result = LOW_SITESECURITYLEVEL - case "mediumLow": - result = MEDIUMLOW_SITESECURITYLEVEL - case "medium": - result = MEDIUM_SITESECURITYLEVEL - case "mediumHigh": - result = MEDIUMHIGH_SITESECURITYLEVEL - case "high": - result = HIGH_SITESECURITYLEVEL - default: - return 0, errors.New("Unknown SiteSecurityLevel value: " + v) - } - return &result, nil + result := USERDEFINED_SITESECURITYLEVEL + switch v { + case "userDefined": + result = USERDEFINED_SITESECURITYLEVEL + case "low": + result = LOW_SITESECURITYLEVEL + case "mediumLow": + result = MEDIUMLOW_SITESECURITYLEVEL + case "medium": + result = MEDIUM_SITESECURITYLEVEL + case "mediumHigh": + result = MEDIUMHIGH_SITESECURITYLEVEL + case "high": + result = HIGH_SITESECURITYLEVEL + default: + return 0, errors.New("Unknown SiteSecurityLevel value: " + v) + } + return &result, nil } func SerializeSiteSecurityLevel(values []SiteSecurityLevel) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/site_settings.go b/src/internal/connector/graph/betasdk/models/site_settings.go index a2a36d94a..1f8930408 100644 --- a/src/internal/connector/graph/betasdk/models/site_settings.go +++ b/src/internal/connector/graph/betasdk/models/site_settings.go @@ -1,123 +1,134 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// SiteSettings +// SiteSettings type SiteSettings struct { - // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. - additionalData map[string]interface{} - // The language tag for the language used on this site. - languageTag *string - // The OdataType property - odataType *string - // Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). - timeZone *string + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // The language tag for the language used on this site. + languageTag *string + // The OdataType property + odataType *string + // Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). + timeZone *string } + // NewSiteSettings instantiates a new siteSettings and sets the default values. -func NewSiteSettings()(*SiteSettings) { - m := &SiteSettings{ - } - m.SetAdditionalData(make(map[string]interface{})); - return m +func NewSiteSettings() *SiteSettings { + m := &SiteSettings{} + m.SetAdditionalData(make(map[string]interface{})) + return m } + // CreateSiteSettingsFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateSiteSettingsFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewSiteSettings(), nil +func CreateSiteSettingsFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewSiteSettings(), nil } + // GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *SiteSettings) GetAdditionalData()(map[string]interface{}) { - return m.additionalData +func (m *SiteSettings) GetAdditionalData() map[string]interface{} { + return m.additionalData } + // GetFieldDeserializers the deserialization information for the current model -func (m *SiteSettings) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) - res["languageTag"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetLanguageTag(val) - } - return nil - } - res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetOdataType(val) - } - return nil - } - res["timeZone"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetTimeZone(val) - } - return nil - } - return res +func (m *SiteSettings) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["languageTag"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetLanguageTag(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["timeZone"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetTimeZone(val) + } + return nil + } + return res } + // GetLanguageTag gets the languageTag property value. The language tag for the language used on this site. -func (m *SiteSettings) GetLanguageTag()(*string) { - return m.languageTag +func (m *SiteSettings) GetLanguageTag() *string { + return m.languageTag } + // GetOdataType gets the @odata.type property value. The OdataType property -func (m *SiteSettings) GetOdataType()(*string) { - return m.odataType +func (m *SiteSettings) GetOdataType() *string { + return m.odataType } + // GetTimeZone gets the timeZone property value. Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). -func (m *SiteSettings) GetTimeZone()(*string) { - return m.timeZone +func (m *SiteSettings) GetTimeZone() *string { + return m.timeZone } + // Serialize serializes information the current object -func (m *SiteSettings) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - { - err := writer.WriteStringValue("languageTag", m.GetLanguageTag()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("@odata.type", m.GetOdataType()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("timeZone", m.GetTimeZone()) - if err != nil { - return err - } - } - { - err := writer.WriteAdditionalData(m.GetAdditionalData()) - if err != nil { - return err - } - } - return nil +func (m *SiteSettings) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteStringValue("languageTag", m.GetLanguageTag()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("timeZone", m.GetTimeZone()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil } + // SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *SiteSettings) SetAdditionalData(value map[string]interface{})() { - m.additionalData = value +func (m *SiteSettings) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value } + // SetLanguageTag sets the languageTag property value. The language tag for the language used on this site. -func (m *SiteSettings) SetLanguageTag(value *string)() { - m.languageTag = value +func (m *SiteSettings) SetLanguageTag(value *string) { + m.languageTag = value } + // SetOdataType sets the @odata.type property value. The OdataType property -func (m *SiteSettings) SetOdataType(value *string)() { - m.odataType = value +func (m *SiteSettings) SetOdataType(value *string) { + m.odataType = value } + // SetTimeZone sets the timeZone property value. Indicates the time offset for the time zone of the site from Coordinated Universal Time (UTC). -func (m *SiteSettings) SetTimeZone(value *string)() { - m.timeZone = value +func (m *SiteSettings) SetTimeZone(value *string) { + m.timeZone = value } diff --git a/src/internal/connector/graph/betasdk/models/site_settingsable.go b/src/internal/connector/graph/betasdk/models/site_settingsable.go index 0423550ea..1b3825e05 100644 --- a/src/internal/connector/graph/betasdk/models/site_settingsable.go +++ b/src/internal/connector/graph/betasdk/models/site_settingsable.go @@ -1,17 +1,17 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// SiteSettingsable +// SiteSettingsable type SiteSettingsable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - GetLanguageTag()(*string) - GetOdataType()(*string) - GetTimeZone()(*string) - SetLanguageTag(value *string)() - SetOdataType(value *string)() - SetTimeZone(value *string)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetLanguageTag() *string + GetOdataType() *string + GetTimeZone() *string + SetLanguageTag(value *string) + SetOdataType(value *string) + SetTimeZone(value *string) } diff --git a/src/internal/connector/graph/betasdk/models/standard_web_part.go b/src/internal/connector/graph/betasdk/models/standard_web_part.go index 0b7b4427a..4532e1d24 100644 --- a/src/internal/connector/graph/betasdk/models/standard_web_part.go +++ b/src/internal/connector/graph/betasdk/models/standard_web_part.go @@ -1,88 +1,96 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// StandardWebPart +// StandardWebPart type StandardWebPart struct { - WebPart - // Data of the webPart. - data WebPartDataable - // A Guid which indicates the type of the webParts - webPartType *string + WebPart + // Data of the webPart. + data WebPartDataable + // A Guid which indicates the type of the webParts + webPartType *string } + // NewStandardWebPart instantiates a new StandardWebPart and sets the default values. -func NewStandardWebPart()(*StandardWebPart) { - m := &StandardWebPart{ - WebPart: *NewWebPart(), - } - odataTypeValue := "#microsoft.graph.standardWebPart"; - m.SetOdataType(&odataTypeValue); - return m +func NewStandardWebPart() *StandardWebPart { + m := &StandardWebPart{ + WebPart: *NewWebPart(), + } + odataTypeValue := "#microsoft.graph.standardWebPart" + m.SetOdataType(&odataTypeValue) + return m } + // CreateStandardWebPartFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateStandardWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewStandardWebPart(), nil +func CreateStandardWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewStandardWebPart(), nil } + // GetData gets the data property value. Data of the webPart. -func (m *StandardWebPart) GetData()(WebPartDataable) { - return m.data +func (m *StandardWebPart) GetData() WebPartDataable { + return m.data } + // GetFieldDeserializers the deserialization information for the current model -func (m *StandardWebPart) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := m.WebPart.GetFieldDeserializers() - res["data"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetObjectValue(CreateWebPartDataFromDiscriminatorValue) - if err != nil { - return err - } - if val != nil { - m.SetData(val.(WebPartDataable)) - } - return nil - } - res["webPartType"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetWebPartType(val) - } - return nil - } - return res +func (m *StandardWebPart) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.WebPart.GetFieldDeserializers() + res["data"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetObjectValue(CreateWebPartDataFromDiscriminatorValue) + if err != nil { + return err + } + if val != nil { + m.SetData(val.(WebPartDataable)) + } + return nil + } + res["webPartType"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetWebPartType(val) + } + return nil + } + return res } + // GetWebPartType gets the webPartType property value. A Guid which indicates the type of the webParts -func (m *StandardWebPart) GetWebPartType()(*string) { - return m.webPartType +func (m *StandardWebPart) GetWebPartType() *string { + return m.webPartType } + // Serialize serializes information the current object -func (m *StandardWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - err := m.WebPart.Serialize(writer) - if err != nil { - return err - } - { - err = writer.WriteObjectValue("data", m.GetData()) - if err != nil { - return err - } - } - { - err = writer.WriteStringValue("webPartType", m.GetWebPartType()) - if err != nil { - return err - } - } - return nil +func (m *StandardWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.WebPart.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteObjectValue("data", m.GetData()) + if err != nil { + return err + } + } + { + err = writer.WriteStringValue("webPartType", m.GetWebPartType()) + if err != nil { + return err + } + } + return nil } + // SetData sets the data property value. Data of the webPart. -func (m *StandardWebPart) SetData(value WebPartDataable)() { - m.data = value +func (m *StandardWebPart) SetData(value WebPartDataable) { + m.data = value } + // SetWebPartType sets the webPartType property value. A Guid which indicates the type of the webParts -func (m *StandardWebPart) SetWebPartType(value *string)() { - m.webPartType = value +func (m *StandardWebPart) SetWebPartType(value *string) { + m.webPartType = value } diff --git a/src/internal/connector/graph/betasdk/models/standard_web_partable.go b/src/internal/connector/graph/betasdk/models/standard_web_partable.go index e09160b2b..b33c25f15 100644 --- a/src/internal/connector/graph/betasdk/models/standard_web_partable.go +++ b/src/internal/connector/graph/betasdk/models/standard_web_partable.go @@ -1,15 +1,15 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// StandardWebPartable +// StandardWebPartable type StandardWebPartable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - WebPartable - GetData()(WebPartDataable) - GetWebPartType()(*string) - SetData(value WebPartDataable)() - SetWebPartType(value *string)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + WebPartable + GetData() WebPartDataable + GetWebPartType() *string + SetData(value WebPartDataable) + SetWebPartType(value *string) } diff --git a/src/internal/connector/graph/betasdk/models/text_web_part.go b/src/internal/connector/graph/betasdk/models/text_web_part.go index f607ffa31..1ae554671 100644 --- a/src/internal/connector/graph/betasdk/models/text_web_part.go +++ b/src/internal/connector/graph/betasdk/models/text_web_part.go @@ -1,62 +1,68 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// TextWebPart +// TextWebPart type TextWebPart struct { - WebPart - // The HTML string in text web part. - innerHtml *string + WebPart + // The HTML string in text web part. + innerHtml *string } + // NewTextWebPart instantiates a new TextWebPart and sets the default values. -func NewTextWebPart()(*TextWebPart) { - m := &TextWebPart{ - WebPart: *NewWebPart(), - } - odataTypeValue := "#microsoft.graph.textWebPart"; - m.SetOdataType(&odataTypeValue); - return m +func NewTextWebPart() *TextWebPart { + m := &TextWebPart{ + WebPart: *NewWebPart(), + } + odataTypeValue := "#microsoft.graph.textWebPart" + m.SetOdataType(&odataTypeValue) + return m } + // CreateTextWebPartFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateTextWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewTextWebPart(), nil +func CreateTextWebPartFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewTextWebPart(), nil } + // GetFieldDeserializers the deserialization information for the current model -func (m *TextWebPart) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := m.WebPart.GetFieldDeserializers() - res["innerHtml"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetInnerHtml(val) - } - return nil - } - return res +func (m *TextWebPart) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := m.WebPart.GetFieldDeserializers() + res["innerHtml"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetInnerHtml(val) + } + return nil + } + return res } + // GetInnerHtml gets the innerHtml property value. The HTML string in text web part. -func (m *TextWebPart) GetInnerHtml()(*string) { - return m.innerHtml +func (m *TextWebPart) GetInnerHtml() *string { + return m.innerHtml } + // Serialize serializes information the current object -func (m *TextWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - err := m.WebPart.Serialize(writer) - if err != nil { - return err - } - { - err = writer.WriteStringValue("innerHtml", m.GetInnerHtml()) - if err != nil { - return err - } - } - return nil +func (m *TextWebPart) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + err := m.WebPart.Serialize(writer) + if err != nil { + return err + } + { + err = writer.WriteStringValue("innerHtml", m.GetInnerHtml()) + if err != nil { + return err + } + } + return nil } + // SetInnerHtml sets the innerHtml property value. The HTML string in text web part. -func (m *TextWebPart) SetInnerHtml(value *string)() { - m.innerHtml = value +func (m *TextWebPart) SetInnerHtml(value *string) { + m.innerHtml = value } diff --git a/src/internal/connector/graph/betasdk/models/text_web_partable.go b/src/internal/connector/graph/betasdk/models/text_web_partable.go index 45e21d92b..f58b6a0c8 100644 --- a/src/internal/connector/graph/betasdk/models/text_web_partable.go +++ b/src/internal/connector/graph/betasdk/models/text_web_partable.go @@ -1,13 +1,13 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// TextWebPartable +// TextWebPartable type TextWebPartable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - WebPartable - GetInnerHtml()(*string) - SetInnerHtml(value *string)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + WebPartable + GetInnerHtml() *string + SetInnerHtml(value *string) } diff --git a/src/internal/connector/graph/betasdk/models/title_area_layout_type.go b/src/internal/connector/graph/betasdk/models/title_area_layout_type.go index 375b68874..3621288a4 100644 --- a/src/internal/connector/graph/betasdk/models/title_area_layout_type.go +++ b/src/internal/connector/graph/betasdk/models/title_area_layout_type.go @@ -1,43 +1,45 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type TitleAreaLayoutType int const ( - IMAGEANDTITLE_TITLEAREALAYOUTTYPE TitleAreaLayoutType = iota - PLAIN_TITLEAREALAYOUTTYPE - COLORBLOCK_TITLEAREALAYOUTTYPE - OVERLAP_TITLEAREALAYOUTTYPE - UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE + IMAGEANDTITLE_TITLEAREALAYOUTTYPE TitleAreaLayoutType = iota + PLAIN_TITLEAREALAYOUTTYPE + COLORBLOCK_TITLEAREALAYOUTTYPE + OVERLAP_TITLEAREALAYOUTTYPE + UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE ) func (i TitleAreaLayoutType) String() string { - return []string{"imageAndTitle", "plain", "colorBlock", "overlap", "unknownFutureValue"}[i] + return []string{"imageAndTitle", "plain", "colorBlock", "overlap", "unknownFutureValue"}[i] } func ParseTitleAreaLayoutType(v string) (interface{}, error) { - result := IMAGEANDTITLE_TITLEAREALAYOUTTYPE - switch v { - case "imageAndTitle": - result = IMAGEANDTITLE_TITLEAREALAYOUTTYPE - case "plain": - result = PLAIN_TITLEAREALAYOUTTYPE - case "colorBlock": - result = COLORBLOCK_TITLEAREALAYOUTTYPE - case "overlap": - result = OVERLAP_TITLEAREALAYOUTTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE - default: - return 0, errors.New("Unknown TitleAreaLayoutType value: " + v) - } - return &result, nil + result := IMAGEANDTITLE_TITLEAREALAYOUTTYPE + switch v { + case "imageAndTitle": + result = IMAGEANDTITLE_TITLEAREALAYOUTTYPE + case "plain": + result = PLAIN_TITLEAREALAYOUTTYPE + case "colorBlock": + result = COLORBLOCK_TITLEAREALAYOUTTYPE + case "overlap": + result = OVERLAP_TITLEAREALAYOUTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_TITLEAREALAYOUTTYPE + default: + return 0, errors.New("Unknown TitleAreaLayoutType value: " + v) + } + return &result, nil } func SerializeTitleAreaLayoutType(values []TitleAreaLayoutType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go b/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go index 27b1e1dba..a34f41dbe 100644 --- a/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go +++ b/src/internal/connector/graph/betasdk/models/title_area_text_alignment_type.go @@ -1,37 +1,39 @@ package models + import ( - "errors" + "errors" ) + // Provides operations to call the remove method. type TitleAreaTextAlignmentType int const ( - LEFT_TITLEAREATEXTALIGNMENTTYPE TitleAreaTextAlignmentType = iota - CENTER_TITLEAREATEXTALIGNMENTTYPE - UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE + LEFT_TITLEAREATEXTALIGNMENTTYPE TitleAreaTextAlignmentType = iota + CENTER_TITLEAREATEXTALIGNMENTTYPE + UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE ) func (i TitleAreaTextAlignmentType) String() string { - return []string{"left", "center", "unknownFutureValue"}[i] + return []string{"left", "center", "unknownFutureValue"}[i] } func ParseTitleAreaTextAlignmentType(v string) (interface{}, error) { - result := LEFT_TITLEAREATEXTALIGNMENTTYPE - switch v { - case "left": - result = LEFT_TITLEAREATEXTALIGNMENTTYPE - case "center": - result = CENTER_TITLEAREATEXTALIGNMENTTYPE - case "unknownFutureValue": - result = UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE - default: - return 0, errors.New("Unknown TitleAreaTextAlignmentType value: " + v) - } - return &result, nil + result := LEFT_TITLEAREATEXTALIGNMENTTYPE + switch v { + case "left": + result = LEFT_TITLEAREATEXTALIGNMENTTYPE + case "center": + result = CENTER_TITLEAREATEXTALIGNMENTTYPE + case "unknownFutureValue": + result = UNKNOWNFUTUREVALUE_TITLEAREATEXTALIGNMENTTYPE + default: + return 0, errors.New("Unknown TitleAreaTextAlignmentType value: " + v) + } + return &result, nil } func SerializeTitleAreaTextAlignmentType(values []TitleAreaTextAlignmentType) []string { - result := make([]string, len(values)) - for i, v := range values { - result[i] = v.String() - } - return result + result := make([]string, len(values)) + for i, v := range values { + result[i] = v.String() + } + return result } diff --git a/src/internal/connector/graph/betasdk/models/web_part_position.go b/src/internal/connector/graph/betasdk/models/web_part_position.go index f2f1c3c9e..f3be0e651 100644 --- a/src/internal/connector/graph/betasdk/models/web_part_position.go +++ b/src/internal/connector/graph/betasdk/models/web_part_position.go @@ -1,175 +1,190 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// WebPartPosition +// WebPartPosition type WebPartPosition struct { - // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. - additionalData map[string]interface{} - // Indicates the identifier of the column where the web part is located. - columnId *float64 - // Indicates the horizontal section where the web part is located. - horizontalSectionId *float64 - // Indicates whether the web part is located in the vertical section. - isInVerticalSection *bool - // The OdataType property - odataType *string - // Index of the current web part. Represents the order of the web part in this column or section. - webPartIndex *float64 + // Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + additionalData map[string]interface{} + // Indicates the identifier of the column where the web part is located. + columnId *float64 + // Indicates the horizontal section where the web part is located. + horizontalSectionId *float64 + // Indicates whether the web part is located in the vertical section. + isInVerticalSection *bool + // The OdataType property + odataType *string + // Index of the current web part. Represents the order of the web part in this column or section. + webPartIndex *float64 } + // NewWebPartPosition instantiates a new webPartPosition and sets the default values. -func NewWebPartPosition()(*WebPartPosition) { - m := &WebPartPosition{ - } - m.SetAdditionalData(make(map[string]interface{})); - return m +func NewWebPartPosition() *WebPartPosition { + m := &WebPartPosition{} + m.SetAdditionalData(make(map[string]interface{})) + return m } + // CreateWebPartPositionFromDiscriminatorValue creates a new instance of the appropriate class based on discriminator value -func CreateWebPartPositionFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { - return NewWebPartPosition(), nil +func CreateWebPartPositionFromDiscriminatorValue(parseNode i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) (i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable, error) { + return NewWebPartPosition(), nil } + // GetAdditionalData gets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *WebPartPosition) GetAdditionalData()(map[string]interface{}) { - return m.additionalData +func (m *WebPartPosition) GetAdditionalData() map[string]interface{} { + return m.additionalData } + // GetColumnId gets the columnId property value. Indicates the identifier of the column where the web part is located. -func (m *WebPartPosition) GetColumnId()(*float64) { - return m.columnId +func (m *WebPartPosition) GetColumnId() *float64 { + return m.columnId } + // GetFieldDeserializers the deserialization information for the current model -func (m *WebPartPosition) GetFieldDeserializers()(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) { - res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode)(error)) - res["columnId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetFloat64Value() - if err != nil { - return err - } - if val != nil { - m.SetColumnId(val) - } - return nil - } - res["horizontalSectionId"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetFloat64Value() - if err != nil { - return err - } - if val != nil { - m.SetHorizontalSectionId(val) - } - return nil - } - res["isInVerticalSection"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetBoolValue() - if err != nil { - return err - } - if val != nil { - m.SetIsInVerticalSection(val) - } - return nil - } - res["@odata.type"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetStringValue() - if err != nil { - return err - } - if val != nil { - m.SetOdataType(val) - } - return nil - } - res["webPartIndex"] = func (n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { - val, err := n.GetFloat64Value() - if err != nil { - return err - } - if val != nil { - m.SetWebPartIndex(val) - } - return nil - } - return res +func (m *WebPartPosition) GetFieldDeserializers() map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + res := make(map[string]func(i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error) + res["columnId"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetColumnId(val) + } + return nil + } + res["horizontalSectionId"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetHorizontalSectionId(val) + } + return nil + } + res["isInVerticalSection"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetBoolValue() + if err != nil { + return err + } + if val != nil { + m.SetIsInVerticalSection(val) + } + return nil + } + res["@odata.type"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetStringValue() + if err != nil { + return err + } + if val != nil { + m.SetOdataType(val) + } + return nil + } + res["webPartIndex"] = func(n i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.ParseNode) error { + val, err := n.GetFloat64Value() + if err != nil { + return err + } + if val != nil { + m.SetWebPartIndex(val) + } + return nil + } + return res } + // GetHorizontalSectionId gets the horizontalSectionId property value. Indicates the horizontal section where the web part is located. -func (m *WebPartPosition) GetHorizontalSectionId()(*float64) { - return m.horizontalSectionId +func (m *WebPartPosition) GetHorizontalSectionId() *float64 { + return m.horizontalSectionId } + // GetIsInVerticalSection gets the isInVerticalSection property value. Indicates whether the web part is located in the vertical section. -func (m *WebPartPosition) GetIsInVerticalSection()(*bool) { - return m.isInVerticalSection +func (m *WebPartPosition) GetIsInVerticalSection() *bool { + return m.isInVerticalSection } + // GetOdataType gets the @odata.type property value. The OdataType property -func (m *WebPartPosition) GetOdataType()(*string) { - return m.odataType +func (m *WebPartPosition) GetOdataType() *string { + return m.odataType } + // GetWebPartIndex gets the webPartIndex property value. Index of the current web part. Represents the order of the web part in this column or section. -func (m *WebPartPosition) GetWebPartIndex()(*float64) { - return m.webPartIndex +func (m *WebPartPosition) GetWebPartIndex() *float64 { + return m.webPartIndex } + // Serialize serializes information the current object -func (m *WebPartPosition) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter)(error) { - { - err := writer.WriteFloat64Value("columnId", m.GetColumnId()) - if err != nil { - return err - } - } - { - err := writer.WriteFloat64Value("horizontalSectionId", m.GetHorizontalSectionId()) - if err != nil { - return err - } - } - { - err := writer.WriteBoolValue("isInVerticalSection", m.GetIsInVerticalSection()) - if err != nil { - return err - } - } - { - err := writer.WriteStringValue("@odata.type", m.GetOdataType()) - if err != nil { - return err - } - } - { - err := writer.WriteFloat64Value("webPartIndex", m.GetWebPartIndex()) - if err != nil { - return err - } - } - { - err := writer.WriteAdditionalData(m.GetAdditionalData()) - if err != nil { - return err - } - } - return nil +func (m *WebPartPosition) Serialize(writer i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.SerializationWriter) error { + { + err := writer.WriteFloat64Value("columnId", m.GetColumnId()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("horizontalSectionId", m.GetHorizontalSectionId()) + if err != nil { + return err + } + } + { + err := writer.WriteBoolValue("isInVerticalSection", m.GetIsInVerticalSection()) + if err != nil { + return err + } + } + { + err := writer.WriteStringValue("@odata.type", m.GetOdataType()) + if err != nil { + return err + } + } + { + err := writer.WriteFloat64Value("webPartIndex", m.GetWebPartIndex()) + if err != nil { + return err + } + } + { + err := writer.WriteAdditionalData(m.GetAdditionalData()) + if err != nil { + return err + } + } + return nil } + // SetAdditionalData sets the additionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. -func (m *WebPartPosition) SetAdditionalData(value map[string]interface{})() { - m.additionalData = value +func (m *WebPartPosition) SetAdditionalData(value map[string]interface{}) { + m.additionalData = value } + // SetColumnId sets the columnId property value. Indicates the identifier of the column where the web part is located. -func (m *WebPartPosition) SetColumnId(value *float64)() { - m.columnId = value +func (m *WebPartPosition) SetColumnId(value *float64) { + m.columnId = value } + // SetHorizontalSectionId sets the horizontalSectionId property value. Indicates the horizontal section where the web part is located. -func (m *WebPartPosition) SetHorizontalSectionId(value *float64)() { - m.horizontalSectionId = value +func (m *WebPartPosition) SetHorizontalSectionId(value *float64) { + m.horizontalSectionId = value } + // SetIsInVerticalSection sets the isInVerticalSection property value. Indicates whether the web part is located in the vertical section. -func (m *WebPartPosition) SetIsInVerticalSection(value *bool)() { - m.isInVerticalSection = value +func (m *WebPartPosition) SetIsInVerticalSection(value *bool) { + m.isInVerticalSection = value } + // SetOdataType sets the @odata.type property value. The OdataType property -func (m *WebPartPosition) SetOdataType(value *string)() { - m.odataType = value +func (m *WebPartPosition) SetOdataType(value *string) { + m.odataType = value } + // SetWebPartIndex sets the webPartIndex property value. Index of the current web part. Represents the order of the web part in this column or section. -func (m *WebPartPosition) SetWebPartIndex(value *float64)() { - m.webPartIndex = value +func (m *WebPartPosition) SetWebPartIndex(value *float64) { + m.webPartIndex = value } diff --git a/src/internal/connector/graph/betasdk/models/web_part_positionable.go b/src/internal/connector/graph/betasdk/models/web_part_positionable.go index f0939db2e..9655ac285 100644 --- a/src/internal/connector/graph/betasdk/models/web_part_positionable.go +++ b/src/internal/connector/graph/betasdk/models/web_part_positionable.go @@ -1,21 +1,21 @@ package models import ( - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" ) -// WebPartPositionable +// WebPartPositionable type WebPartPositionable interface { - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder - i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable - GetColumnId()(*float64) - GetHorizontalSectionId()(*float64) - GetIsInVerticalSection()(*bool) - GetOdataType()(*string) - GetWebPartIndex()(*float64) - SetColumnId(value *float64)() - SetHorizontalSectionId(value *float64)() - SetIsInVerticalSection(value *bool)() - SetOdataType(value *string)() - SetWebPartIndex(value *float64)() + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.AdditionalDataHolder + i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91.Parsable + GetColumnId() *float64 + GetHorizontalSectionId() *float64 + GetIsInVerticalSection() *bool + GetOdataType() *string + GetWebPartIndex() *float64 + SetColumnId(value *float64) + SetHorizontalSectionId(value *float64) + SetIsInVerticalSection(value *bool) + SetOdataType(value *string) + SetWebPartIndex(value *float64) } From 4ac81e6253469060bd2ba3e93e0fb76cb779ab44 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 18:47:49 -0700 Subject: [PATCH 21/46] print location of log file (#2343) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual --- src/pkg/logger/logger.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index 0d5ffe250..b76b46070 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -7,6 +7,7 @@ import ( "time" "github.com/alcionai/clues" + "github.com/alcionai/corso/src/cli/print" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.uber.org/zap" @@ -118,6 +119,7 @@ func PreloadLoggingFlags() (string, string) { if logfile != "stdout" && logfile != "stderr" { logdir := filepath.Dir(logfile) + print.Info(context.Background(), "Logging to file: "+logfile) err := os.MkdirAll(logdir, 0o755) if err != nil { From ec7d3c6fc5568015c9f57e32b13a06c8656b5b1e Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 19:19:44 -0700 Subject: [PATCH 22/46] add logging around operation completion (#2337) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual --- src/cli/cli.go | 4 ++ src/go.mod | 2 +- src/go.sum | 4 +- src/internal/operations/backup.go | 85 +++++++++++++++------------- src/internal/operations/manifests.go | 12 ++-- src/internal/operations/restore.go | 40 ++++++++----- src/pkg/logger/logger.go | 2 +- src/pkg/repository/repository.go | 25 ++++---- 8 files changed, 103 insertions(+), 71 deletions(-) diff --git a/src/cli/cli.go b/src/cli/cli.go index b67663d06..f202e4953 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -6,6 +6,7 @@ import ( "regexp" "strings" + "github.com/alcionai/clues" "github.com/spf13/cobra" "github.com/alcionai/corso/src/cli/backup" @@ -121,6 +122,9 @@ func Handle() { }() if err := corsoCmd.ExecuteContext(ctx); err != nil { + logger.Ctx(ctx). + With("err", err). + Errorw("cli execution", clues.InErr(err).Slice()...) os.Exit(1) } } diff --git a/src/go.mod b/src/go.mod index a8054ab0e..0b7fd24ee 100644 --- a/src/go.mod +++ b/src/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 - github.com/alcionai/clues v0.0.0-20230120231953-1cf61dbafc40 + github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 github.com/aws/aws-sdk-go v1.44.190 github.com/aws/aws-xray-sdk-go v1.8.0 github.com/google/uuid v1.3.0 diff --git a/src/go.sum b/src/go.sum index 72af64b2d..aa032076f 100644 --- a/src/go.sum +++ b/src/go.sum @@ -52,8 +52,8 @@ github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1o github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= -github.com/alcionai/clues v0.0.0-20230120231953-1cf61dbafc40 h1:bvAwz0dcJeIyRjudVyzmmawOvc4SqlSerKd0B4dh0yw= -github.com/alcionai/clues v0.0.0-20230120231953-1cf61dbafc40/go.mod h1:UlAs8jkWIpsOMakiC8NxPgQQVQRdvyf1hYMszlYYLb4= +github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 h1:eTgICcmcydEWG8J+hgnidf0pzujV3Gd2XqmknykZkzA= +github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005/go.mod h1:UlAs8jkWIpsOMakiC8NxPgQQVQRdvyf1hYMszlYYLb4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 51e92181c..8e40d820f 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -40,6 +40,9 @@ type BackupOperation struct { Version string `json:"version"` account account.Account + + // when true, this allows for incremental backups instead of full data pulls + incremental bool } // BackupResults aggregate the details of the result of the operation. @@ -66,6 +69,7 @@ func NewBackupOperation( Selectors: selector, Version: "v0", account: acct, + incremental: useIncrementalBackup(selector, opts), } if err := op.validate(); err != nil { return BackupOperation{}, err @@ -102,10 +106,36 @@ type detailsWriter interface { // --------------------------------------------------------------------------- // Run begins a synchronous backup operation. -func (op *BackupOperation) Run(ctx context.Context) (err error) { +func (op *BackupOperation) Run(ctx context.Context) error { ctx, end := D.Span(ctx, "operations:backup:run") - defer end() + defer func() { + end() + // wait for the progress display to clean up + observe.Complete() + }() + ctx = clues.AddAll( + ctx, + "tenant_id", op.account.ID(), // TODO: pii + "resource_owner", op.ResourceOwner, // TODO: pii + "backup_id", op.Results.BackupID, + "service", op.Selectors.Service, + "incremental", op.incremental) + + if err := op.do(ctx); err != nil { + logger.Ctx(ctx). + With("err", err). + Errorw("backup operation", clues.InErr(err).Slice()...) + + return err + } + + logger.Ctx(ctx).Infow("completed backup", "results", op.Results) + + return nil +} + +func (op *BackupOperation) do(ctx context.Context) (err error) { var ( opStats backupStats backupDetails *details.Builder @@ -114,19 +144,10 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { startTime = time.Now() detailsStore = streamstore.New(op.kopia, tenantID, op.Selectors.PathService()) reasons = selectorToReasons(op.Selectors) - uib = useIncrementalBackup(op.Selectors, op.Options) ) op.Results.BackupID = model.StableID(uuid.NewString()) - ctx = clues.AddAll( - ctx, - "tenant_id", tenantID, // TODO: pii - "resource_owner", op.ResourceOwner, // TODO: pii - "backup_id", op.Results.BackupID, - "service", op.Selectors.Service, - "incremental", uib) - op.bus.Event( ctx, events.BackupStart, @@ -139,9 +160,6 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { // persist operation results to the model store on exit defer func() { - // wait for the progress display to clean up - observe.Complete() - err = op.persistResults(startTime, &opStats) if err != nil { op.Errors.Fail(errors.Wrap(err, "persisting backup results")) @@ -165,7 +183,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { op.store, reasons, tenantID, - uib, + op.incremental, op.Errors) if err != nil { op.Errors.Fail(errors.Wrap(err, "collecting manifest heuristics")) @@ -190,7 +208,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { return opStats.readErr } - ctx = clues.Add(ctx, "collections", len(cs)) + ctx = clues.Add(ctx, "coll_count", len(cs)) opStats.k, backupDetails, toMerge, err = consumeBackupDataCollections( ctx, @@ -200,7 +218,7 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { mans, cs, op.Results.BackupID, - uib && canUseMetaData) + op.incremental && canUseMetaData) if err != nil { op.Errors.Fail(errors.Wrap(err, "backing up service data")) opStats.writeErr = op.Errors.Err() @@ -208,11 +226,6 @@ func (op *BackupOperation) Run(ctx context.Context) (err error) { return opStats.writeErr } - logger.Ctx(ctx).Debugf( - "Backed up %d directories and %d files", - opStats.k.TotalDirectoryCount, opStats.k.TotalFileCount, - ) - if err = mergeDetails( ctx, op.store, @@ -334,7 +347,7 @@ func builderFromReason(ctx context.Context, tenant string, r kopia.Reason) (*pat false, ) if err != nil { - return nil, clues.Wrap(err, "building path").WithMap(clues.Values(ctx)) + return nil, clues.Wrap(err, "building path").WithClues(ctx) } return p.ToBuilder().Dir(), nil @@ -404,13 +417,9 @@ func consumeBackupDataCollections( logger.Ctx(ctx).Infow( "using base for backup", - "snapshot_id", - m.ID, - "services", - svcs, - "categories", - cats, - ) + "snapshot_id", m.ID, + "services", svcs, + "categories", cats) } kopiaStats, deets, itemsSourcedFromBase, err := bu.BackupCollections( @@ -481,7 +490,7 @@ func mergeDetails( bID, ok := man.GetTag(kopia.TagBackupID) if !ok { - return clues.New("no backup ID in snapshot manifest").WithMap(clues.Values(mctx)) + return clues.New("no backup ID in snapshot manifest").WithClues(mctx) } mctx = clues.Add(mctx, "manifest_backup_id", bID) @@ -493,14 +502,14 @@ func mergeDetails( detailsStore, ) if err != nil { - return clues.New("fetching base details for backup").WithMap(clues.Values(mctx)) + return clues.New("fetching base details for backup").WithClues(mctx) } for _, entry := range baseDeets.Items() { rr, err := path.FromDataLayerPath(entry.RepoRef, true) if err != nil { return clues.New("parsing base item info path"). - WithMap(clues.Values(mctx)). + WithClues(mctx). With("repo_ref", entry.RepoRef) // todo: pii } @@ -524,7 +533,7 @@ func mergeDetails( // Fixup paths in the item. item := entry.ItemInfo if err := details.UpdateItem(&item, newPath); err != nil { - return clues.New("updating item details").WithMap(clues.Values(mctx)) + return clues.New("updating item details").WithClues(mctx) } // TODO(ashmrtn): This may need updated if we start using this merge @@ -550,7 +559,7 @@ func mergeDetails( if addedEntries != len(shortRefsFromPrevBackup) { return clues.New("incomplete migration of backup details"). - WithMap(clues.Values(ctx)). + WithClues(ctx). WithAll("item_count", addedEntries, "expected_item_count", len(shortRefsFromPrevBackup)) } @@ -603,12 +612,12 @@ func (op *BackupOperation) createBackupModels( ctx = clues.Add(ctx, "snapshot_id", snapID) if backupDetails == nil { - return clues.New("no backup details to record").WithMap(clues.Values(ctx)) + return clues.New("no backup details to record").WithClues(ctx) } detailsID, err := detailsStore.WriteBackupDetails(ctx, backupDetails) if err != nil { - return clues.Wrap(err, "creating backupDetails model").WithMap(clues.Values(ctx)) + return clues.Wrap(err, "creating backupDetails model").WithClues(ctx) } ctx = clues.Add(ctx, "details_id", detailsID) @@ -622,7 +631,7 @@ func (op *BackupOperation) createBackupModels( ) if err = op.store.Put(ctx, model.BackupSchema, b); err != nil { - return clues.Wrap(err, "creating backup model").WithMap(clues.Values(ctx)) + return clues.Wrap(err, "creating backup model").WithClues(ctx) } dur := op.Results.CompletedAt.Sub(op.Results.StartedAt) diff --git a/src/internal/operations/manifests.go b/src/internal/operations/manifests.go index 9c765fe70..c0ba35e43 100644 --- a/src/internal/operations/manifests.go +++ b/src/internal/operations/manifests.go @@ -73,7 +73,7 @@ func produceManifestsAndMetadata( if err := verifyDistinctBases(ctx, ms, errs); err != nil { logger.Ctx(ctx).With("error", err).Infow( "base snapshot collision, falling back to full backup", - clues.Slice(ctx)...) + clues.In(ctx).Slice()...) return ms, nil, false, nil } @@ -87,7 +87,7 @@ func produceManifestsAndMetadata( bID, ok := man.GetTag(kopia.TagBackupID) if !ok { - err = clues.New("snapshot manifest missing backup ID").WithMap(clues.Values(mctx)) + err = clues.New("snapshot manifest missing backup ID").WithClues(ctx) return nil, nil, false, err } @@ -98,7 +98,7 @@ func produceManifestsAndMetadata( // if no backup exists for any of the complete manifests, we want // to fall back to a complete backup. if errors.Is(err, kopia.ErrNotFound) { - logger.Ctx(ctx).Infow("backup missing, falling back to full backup", clues.Slice(mctx)...) + logger.Ctx(ctx).Infow("backup missing, falling back to full backup", clues.In(mctx).Slice()...) return ms, nil, false, nil } @@ -113,7 +113,7 @@ func produceManifestsAndMetadata( // This makes an assumption that the ID points to a populated set of // details; we aren't doing the work to look them up. if len(dID) == 0 { - logger.Ctx(ctx).Infow("backup missing details ID, falling back to full backup", clues.Slice(mctx)...) + logger.Ctx(ctx).Infow("backup missing details ID, falling back to full backup", clues.In(mctx).Slice()...) return ms, nil, false, nil } @@ -159,7 +159,7 @@ func verifyDistinctBases(ctx context.Context, mans []*kopia.ManifestEntry, errs failed = true errs.Add(clues.New("manifests have overlapping reasons"). - WithMap(clues.Values(ctx)). + WithClues(ctx). With("other_manifest_id", b)) continue @@ -170,7 +170,7 @@ func verifyDistinctBases(ctx context.Context, mans []*kopia.ManifestEntry, errs } if failed { - return clues.New("multiple base snapshots qualify").WithMap(clues.Values(ctx)) + return clues.New("multiple base snapshots qualify").WithClues(ctx) } return nil diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index db5ca9a93..30bc303a9 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -107,8 +107,33 @@ type restorer interface { // Run begins a synchronous restore operation. func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.Details, err error) { ctx, end := D.Span(ctx, "operations:restore:run") - defer end() + defer func() { + end() + // wait for the progress display to clean up + observe.Complete() + }() + ctx = clues.AddAll( + ctx, + "tenant_id", op.account.ID(), // TODO: pii + "backup_id", op.BackupID, + "service", op.Selectors.Service) + + deets, err := op.do(ctx) + if err != nil { + logger.Ctx(ctx). + With("err", err). + Errorw("restore operation", clues.InErr(err).Slice()...) + + return nil, err + } + + logger.Ctx(ctx).Infow("completed restore", "results", op.Results) + + return deets, nil +} + +func (op *RestoreOperation) do(ctx context.Context) (restoreDetails *details.Details, err error) { var ( opStats = restoreStats{ bytesRead: &stats.ByteCounter{}, @@ -118,9 +143,6 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De ) defer func() { - // wait for the progress display to clean up - observe.Complete() - err = op.persistResults(ctx, startTime, &opStats) if err != nil { return @@ -129,12 +151,6 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De detailsStore := streamstore.New(op.kopia, op.account.ID(), op.Selectors.PathService()) - ctx = clues.AddAll( - ctx, - "tenant_id", op.account.ID(), // TODO: pii - "backup_id", op.BackupID, - "service", op.Selectors.Service) - bup, deets, err := getBackupAndDetailsFromID( ctx, op.BackupID, @@ -166,7 +182,6 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De } ctx = clues.Add(ctx, "details_paths", len(paths)) - observe.Message(ctx, observe.Safe(fmt.Sprintf("Discovered %d items in backup %s to restore", len(paths), op.BackupID))) kopiaComplete, closer := observe.MessageWithCompletion(ctx, observe.Safe("Enumerating items in repository")) @@ -180,8 +195,7 @@ func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.De } kopiaComplete <- struct{}{} - ctx = clues.Add(ctx, "collections", len(dcs)) - + ctx = clues.Add(ctx, "coll_count", len(dcs)) opStats.cs = dcs opStats.resourceCount = len(data.ResourceOwnerSet(dcs)) diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index b76b46070..923af44c8 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -267,7 +267,7 @@ func Ctx(ctx context.Context) *zap.SugaredLogger { return singleton(levelOf(llFlag), defaultLogLocation()) } - return l.(*zap.SugaredLogger).With(clues.Slice(ctx)...) + return l.(*zap.SugaredLogger).With(clues.In(ctx).Slice()...) } // transforms the llevel flag value to a logLevel enum diff --git a/src/pkg/repository/repository.go b/src/pkg/repository/repository.go index f8c8d3d49..087b193bc 100644 --- a/src/pkg/repository/repository.go +++ b/src/pkg/repository/repository.go @@ -2,11 +2,12 @@ package repository import ( "context" - "errors" "time" + "github.com/alcionai/clues" "github.com/google/uuid" "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/events" "github.com/alcionai/corso/src/internal/kopia" @@ -88,6 +89,8 @@ func Initialize( s storage.Storage, opts control.Options, ) (Repository, error) { + ctx = clues.AddAll(ctx, "acct_provider", acct.Provider, "storage_provider", s.Provider) + kopiaRef := kopia.NewConn(s) if err := kopiaRef.Initialize(ctx); err != nil { // replace common internal errors so that sdk users can check results with errors.Is() @@ -95,7 +98,7 @@ func Initialize( return nil, ErrorRepoAlreadyExists } - return nil, err + return nil, errors.Wrap(err, "initializing kopia") } // kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe // to close here. @@ -103,17 +106,17 @@ func Initialize( w, err := kopia.NewWrapper(kopiaRef) if err != nil { - return nil, err + return nil, clues.Stack(err).WithClues(ctx) } ms, err := kopia.NewModelStore(kopiaRef) if err != nil { - return nil, err + return nil, clues.Stack(err).WithClues(ctx) } bus, err := events.NewBus(ctx, s, acct.ID(), opts) if err != nil { - return nil, err + return nil, errors.Wrap(err, "constructing event bus") } repoID := newRepoID(s) @@ -131,7 +134,7 @@ func Initialize( } if err := newRepoModel(ctx, ms, r.ID); err != nil { - return nil, errors.New("setting up repository") + return nil, clues.New("setting up repository").WithClues(ctx) } r.Bus.Event(ctx, events.RepoInit, nil) @@ -150,6 +153,8 @@ func Connect( s storage.Storage, opts control.Options, ) (Repository, error) { + ctx = clues.AddAll(ctx, "acct_provider", acct.Provider, "storage_provider", s.Provider) + // Close/Reset the progress bar. This ensures callers don't have to worry about // their output getting clobbered (#1720) defer observe.Complete() @@ -160,7 +165,7 @@ func Connect( kopiaRef := kopia.NewConn(s) if err := kopiaRef.Connect(ctx); err != nil { - return nil, err + return nil, errors.Wrap(err, "connecting kopia client") } // kopiaRef comes with a count of 1 and NewWrapper/NewModelStore bumps it again so safe // to close here. @@ -168,17 +173,17 @@ func Connect( w, err := kopia.NewWrapper(kopiaRef) if err != nil { - return nil, err + return nil, clues.Stack(err).WithClues(ctx) } ms, err := kopia.NewModelStore(kopiaRef) if err != nil { - return nil, err + return nil, clues.Stack(err).WithClues(ctx) } bus, err := events.NewBus(ctx, s, acct.ID(), opts) if err != nil { - return nil, err + return nil, errors.Wrap(err, "constructing event bus") } rm, err := getRepoModel(ctx, ms) From 7ee1575dc2fa96cb3ac2d11b756bed34325d88e8 Mon Sep 17 00:00:00 2001 From: Keepers Date: Tue, 31 Jan 2023 19:41:06 -0700 Subject: [PATCH 23/46] log end-of-observe even when hidden (#2339) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual --- src/internal/observe/observe.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/internal/observe/observe.go b/src/internal/observe/observe.go index d8492109d..2b86066f7 100644 --- a/src/internal/observe/observe.go +++ b/src/internal/observe/observe.go @@ -177,7 +177,7 @@ func MessageWithCompletion( completionCh := make(chan struct{}, 1) if cfg.hidden() { - return completionCh, func() {} + return completionCh, func() { log.Info("done - " + clean) } } wg.Add(1) @@ -232,7 +232,7 @@ func ItemProgress( log.Debug(header) if cfg.hidden() || rc == nil || totalBytes == 0 { - return rc, func() {} + return rc, func() { log.Debug("done - " + header) } } wg.Add(1) @@ -286,7 +286,7 @@ func ProgressWithCount( } }(progressCh) - return progressCh, func() {} + return progressCh, func() { log.Info("done - " + lmsg) } } wg.Add(1) @@ -381,16 +381,19 @@ func CollectionProgress( if cfg.hidden() || len(user.String()) == 0 || len(dirName.String()) == 0 { ch := make(chan struct{}) + counted := 0 + go func(ci <-chan struct{}) { for { _, ok := <-ci if !ok { return } + counted++ } }(ch) - return ch, func() {} + return ch, func() { log.Infow("done - "+message, "count", counted) } } wg.Add(1) From 4ab504056900ed709041736aead9c69356ce52e8 Mon Sep 17 00:00:00 2001 From: Vaibhav Kamra Date: Tue, 31 Jan 2023 19:39:00 -0800 Subject: [PATCH 24/46] Add progress logging to delta queries (#2345) ## Description Adds logging to delta enumeration to log every 1000 items or so (each page is set to 200 items) ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- src/internal/connector/exchange/api/contacts.go | 7 +++++++ src/internal/connector/exchange/api/events.go | 7 +++++++ src/internal/connector/exchange/api/mail.go | 7 +++++++ src/internal/connector/exchange/api/shared.go | 13 ++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/internal/connector/exchange/api/contacts.go b/src/internal/connector/exchange/api/contacts.go index 33f05d2a3..185bf6e22 100644 --- a/src/internal/connector/exchange/api/contacts.go +++ b/src/internal/connector/exchange/api/contacts.go @@ -12,10 +12,12 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" + "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/pkg/backup/details" + "github.com/alcionai/corso/src/pkg/selectors" ) // --------------------------------------------------------------------------- @@ -234,6 +236,11 @@ func (c Contacts) GetAddedAndRemovedItemIDs( resetDelta bool ) + ctx = clues.AddAll( + ctx, + "category", selectors.ExchangeContact, + "folder_id", directoryID) + options, err := optionsForContactFoldersItemDelta([]string{"parentFolderId"}) if err != nil { return nil, nil, DeltaUpdate{}, errors.Wrap(err, "getting query options") diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index 63545143d..962b2e576 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -12,6 +12,7 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" + "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" @@ -19,6 +20,7 @@ import ( "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/path" + "github.com/alcionai/corso/src/pkg/selectors" ) // --------------------------------------------------------------------------- @@ -271,6 +273,11 @@ func (c Events) GetAddedAndRemovedItemIDs( errs *multierror.Error ) + ctx = clues.AddAll( + ctx, + "category", selectors.ExchangeEvent, + "calendar_id", calendarID) + if len(oldDelta) > 0 { builder := users.NewItemCalendarsItemEventsDeltaRequestBuilder(oldDelta, service.Adapter()) pgr := &eventPager{service, builder, nil} diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index 597676874..29c03b82f 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -12,11 +12,13 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" + "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/logger" + "github.com/alcionai/corso/src/pkg/selectors" ) // --------------------------------------------------------------------------- @@ -259,6 +261,11 @@ func (c Mail) GetAddedAndRemovedItemIDs( resetDelta bool ) + ctx = clues.AddAll( + ctx, + "category", selectors.ExchangeMail, + "folder_id", directoryID) + options, err := optionsForFolderMessagesDelta([]string{"isRead"}) if err != nil { return nil, nil, DeltaUpdate{}, errors.Wrap(err, "getting query options") diff --git a/src/internal/connector/exchange/api/shared.go b/src/internal/connector/exchange/api/shared.go index 9cb4d8ab0..e4d563e90 100644 --- a/src/internal/connector/exchange/api/shared.go +++ b/src/internal/connector/exchange/api/shared.go @@ -65,6 +65,9 @@ func getItemsAddedAndRemovedFromContainer( deltaURL string ) + itemCount := 0 + page := 0 + for { // get the next page of data, check for standard errors resp, err := pager.getPage(ctx) @@ -83,7 +86,13 @@ func getItemsAddedAndRemovedFromContainer( return nil, nil, "", err } - logger.Ctx(ctx).Infow("Got page", "items", len(items)) + itemCount += len(items) + page++ + + // Log every ~1000 items (the page size we use is 200) + if page%5 == 0 { + logger.Ctx(ctx).Infow("queried items", "count", itemCount) + } // iterate through the items in the page for _, item := range items { @@ -117,5 +126,7 @@ func getItemsAddedAndRemovedFromContainer( pager.setNext(nextLink) } + logger.Ctx(ctx).Infow("completed enumeration", "count", itemCount) + return addedIDs, removedIDs, deltaURL, nil } From 2873befe536358634a69c544d61528eda4734d06 Mon Sep 17 00:00:00 2001 From: Vaibhav Kamra Date: Tue, 31 Jan 2023 19:57:19 -0800 Subject: [PATCH 25/46] Log collection updates (#2346) ## Description Adds a log message to collection updates to indicate how many items we have streamed into the repository ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- src/internal/observe/observe.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/internal/observe/observe.go b/src/internal/observe/observe.go index 2b86066f7..34da29331 100644 --- a/src/internal/observe/observe.go +++ b/src/internal/observe/observe.go @@ -390,6 +390,11 @@ func CollectionProgress( return } counted++ + + // Log every 1000 items that are processed + if counted%1000 == 0 { + log.Infow("uploading", "count", counted) + } } }(ch) @@ -435,6 +440,11 @@ func CollectionProgress( counted++ + // Log every 1000 items that are processed + if counted%1000 == 0 { + log.Infow("uploading", "count", counted) + } + bar.Increment() } } From 45eb71f1c2d6ce3dc7a4d5685040d26bedf398bd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 05:18:49 +0000 Subject: [PATCH 26/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20sass=20from?= =?UTF-8?q?=201.57.1=20to=201.58.0=20in=20/website=20(#2350)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/package-lock.json | 14 +++++++------- website/package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index 11a0d15e1..100008906 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -24,7 +24,7 @@ "prism-react-renderer": "^1.3.5", "react": "^17.0.2", "react-dom": "^17.0.2", - "sass": "^1.57.1", + "sass": "^1.58.0", "tw-elements": "^1.0.0-alpha13", "wow.js": "^1.2.2" }, @@ -11861,9 +11861,9 @@ "license": "MIT" }, "node_modules/sass": { - "version": "1.57.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz", - "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==", + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", + "integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -22452,9 +22452,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.57.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.57.1.tgz", - "integrity": "sha512-O2+LwLS79op7GI0xZ8fqzF7X2m/m8WFfI02dHOdsK5R2ECeS5F62zrwg/relM1rjSLy7Vd/DiMNIvPrQGsA0jw==", + "version": "1.58.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.0.tgz", + "integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==", "requires": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", diff --git a/website/package.json b/website/package.json index 5d5670674..08e24f415 100644 --- a/website/package.json +++ b/website/package.json @@ -30,7 +30,7 @@ "prism-react-renderer": "^1.3.5", "react": "^17.0.2", "react-dom": "^17.0.2", - "sass": "^1.57.1", + "sass": "^1.58.0", "tw-elements": "^1.0.0-alpha13", "wow.js": "^1.2.2" }, From d918001ee47951331f5018df8e3b4bdefe70d85e Mon Sep 17 00:00:00 2001 From: Niraj Tolia Date: Tue, 31 Jan 2023 21:32:17 -0800 Subject: [PATCH 27/46] Improve log file docs (#2347) ## Description Add some nuance to log file docs. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 --- website/docs/setup/configuration.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/docs/setup/configuration.md b/website/docs/setup/configuration.md index 85c99c6bb..d9255f6b7 100644 --- a/website/docs/setup/configuration.md +++ b/website/docs/setup/configuration.md @@ -129,7 +129,9 @@ directory within the container. ## Log Files +Corso generates a unique log file named with its timestamp for every invocation. The default location of Corso's log file is shown below but the location can be overridden by using the `--log-file` flag. +The log file will be appended to if multiple Corso invocations are pointed to the same file. You can also use `stdout` or `stderr` as the `--log-file` location to redirect the logs to "stdout" and "stderr" respectively. From 3fd3da7cafc15678ed5d42f2a1ac5f5135f7d500 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Feb 2023 16:33:53 +0000 Subject: [PATCH 28/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20github.com/aw?= =?UTF-8?q?s/aws-sdk-go=20from=201.44.190=20to=201.44.191=20in=20/src=20(#?= =?UTF-8?q?2351)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.190 to 1.44.191.
Release notes

Sourced from github.com/aws/aws-sdk-go's releases.

Release v1.44.191 (2023-01-31)

Service Client Updates

  • service/accessanalyzer: Adds new service
  • service/appsync: Updates service API and documentation
  • service/cloudtrail: Updates service API and documentation
    • Add new "Channel" APIs to enable users to manage channels used for CloudTrail Lake integrations, and "Resource Policy" APIs to enable users to manage the resource-based permissions policy attached to a channel.
  • service/cloudtrail-data: Adds new service
  • service/codeartifact: Updates service API and documentation
  • service/connectparticipant: Adds new service
  • service/ec2: Updates service API and documentation
    • This launch allows customers to associate up to 8 IP addresses to their NAT Gateways to increase the limit on concurrent connections to a single destination by eight times from 55K to 440K.
  • service/groundstation: Updates service API and documentation
  • service/iot: Updates service API and documentation
    • Added support for IoT Rules Engine Cloudwatch Logs action batch mode.
  • service/kinesis: Adds new service
    • Enabled FIPS endpoints for GovCloud (US) regions in SDK.
  • service/opensearch: Updates service API and documentation
  • service/outposts: Adds new service
  • service/polly: Updates service API
    • Amazon Polly adds two new neural American English voices - Ruth, Stephen
  • service/sagemaker: Updates service API and documentation
    • Amazon SageMaker Automatic Model Tuning now supports more completion criteria for Hyperparameter Optimization.
  • service/securityhub: Updates service API and documentation
  • service/support: Adds new service
    • This fixes incorrect endpoint construction when a customer is explicitly setting a region.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go&package-manager=go_modules&previous-version=1.44.190&new-version=1.44.191)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- src/go.mod | 2 +- src/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/go.mod b/src/go.mod index 0b7fd24ee..f947ed45a 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 - github.com/aws/aws-sdk-go v1.44.190 + github.com/aws/aws-sdk-go v1.44.191 github.com/aws/aws-xray-sdk-go v1.8.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/src/go.sum b/src/go.sum index aa032076f..02f39a5a5 100644 --- a/src/go.sum +++ b/src/go.sum @@ -62,8 +62,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/aws/aws-sdk-go v1.44.190 h1:QC+Pf/Ooj7Waf2obOPZbIQOqr00hy4h54j3ZK9mvHcc= -github.com/aws/aws-sdk-go v1.44.190/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.191 h1:GnbkalCx/AgobaorDMFCa248acmk+91+aHBQOk7ljzU= +github.com/aws/aws-sdk-go v1.44.191/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-xray-sdk-go v1.8.0 h1:0xncHZ588wB/geLjbM/esoW3FOEThWy2TJyb4VXfLFY= github.com/aws/aws-xray-sdk-go v1.8.0/go.mod h1:7LKe47H+j3evfvS1+q0wzpoaGXGrF3mUsfM+thqVO+A= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= From 1594a86c223d45ae410e1dbeec39993d0048450d Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Wed, 1 Feb 2023 09:03:23 -0800 Subject: [PATCH 29/46] OneDrive Items API for mocking (#2322) ## Description Create a pager for drive items that allows for better testing via mocking. Increased testing will come in later PRs ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [x] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2264 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [x] :green_heart: E2E --- src/internal/connector/onedrive/api/drive.go | 80 +++++++++++++---- .../connector/onedrive/collections.go | 14 ++- src/internal/connector/onedrive/drive.go | 88 +++++++++++-------- src/internal/connector/onedrive/item_test.go | 12 ++- 4 files changed, 137 insertions(+), 57 deletions(-) diff --git a/src/internal/connector/onedrive/api/drive.go b/src/internal/connector/onedrive/api/drive.go index 6dd7d46a1..fea6e53a7 100644 --- a/src/internal/connector/onedrive/api/drive.go +++ b/src/internal/connector/onedrive/api/drive.go @@ -3,6 +3,7 @@ package api import ( "context" + msdrives "github.com/microsoftgraph/msgraph-sdk-go/drives" "github.com/microsoftgraph/msgraph-sdk-go/models" mssites "github.com/microsoftgraph/msgraph-sdk-go/sites" msusers "github.com/microsoftgraph/msgraph-sdk-go/users" @@ -12,6 +13,65 @@ import ( "github.com/alcionai/corso/src/internal/connector/graph/api" ) +func getValues[T any](l api.PageLinker) ([]T, error) { + page, ok := l.(interface{ GetValue() []T }) + if !ok { + return nil, errors.Errorf( + "response of type [%T] does not comply with GetValue() interface", + l, + ) + } + + return page.GetValue(), nil +} + +// max we can do is 999 +const pageSize = int32(999) + +type driveItemPager struct { + gs graph.Servicer + builder *msdrives.ItemRootDeltaRequestBuilder + options *msdrives.ItemRootDeltaRequestBuilderGetRequestConfiguration +} + +func NewItemPager( + gs graph.Servicer, + driveID, link string, + fields []string, +) *driveItemPager { + pageCount := pageSize + requestConfig := &msdrives.ItemRootDeltaRequestBuilderGetRequestConfiguration{ + QueryParameters: &msdrives.ItemRootDeltaRequestBuilderGetQueryParameters{ + Top: &pageCount, + Select: fields, + }, + } + + res := &driveItemPager{ + gs: gs, + options: requestConfig, + builder: gs.Client().DrivesById(driveID).Root().Delta(), + } + + if len(link) > 0 { + res.builder = msdrives.NewItemRootDeltaRequestBuilder(link, gs.Adapter()) + } + + return res +} + +func (p *driveItemPager) GetPage(ctx context.Context) (api.DeltaPageLinker, error) { + return p.builder.Get(ctx, p.options) +} + +func (p *driveItemPager) SetNext(link string) { + p.builder = msdrives.NewItemRootDeltaRequestBuilder(link, p.gs.Adapter()) +} + +func (p *driveItemPager) ValuesIn(l api.DeltaPageLinker) ([]models.DriveItemable, error) { + return getValues[models.DriveItemable](l) +} + type userDrivePager struct { gs graph.Servicer builder *msusers.ItemDrivesRequestBuilder @@ -47,15 +107,7 @@ func (p *userDrivePager) SetNext(link string) { } func (p *userDrivePager) ValuesIn(l api.PageLinker) ([]models.Driveable, error) { - page, ok := l.(interface{ GetValue() []models.Driveable }) - if !ok { - return nil, errors.Errorf( - "response of type [%T] does not comply with GetValue() interface", - l, - ) - } - - return page.GetValue(), nil + return getValues[models.Driveable](l) } type siteDrivePager struct { @@ -93,13 +145,5 @@ func (p *siteDrivePager) SetNext(link string) { } func (p *siteDrivePager) ValuesIn(l api.PageLinker) ([]models.Driveable, error) { - page, ok := l.(interface{ GetValue() []models.Driveable }) - if !ok { - return nil, errors.Errorf( - "response of type [%T] does not comply with GetValue() interface", - l, - ) - } - - return page.GetValue(), nil + return getValues[models.Driveable](l) } diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index 35b81f7f2..1e36b1ea3 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -65,6 +65,13 @@ type Collections struct { // for a OneDrive folder CollectionMap map[string]data.Collection + // Not the most ideal, but allows us to change the pager function for testing + // as needed. This will allow us to mock out some scenarios during testing. + itemPagerFunc func( + servicer graph.Servicer, + driveID, link string, + ) itemPager + // Track stats from drive enumeration. Represents the items backed up. NumItems int NumFiles int @@ -88,6 +95,7 @@ func NewCollections( source: source, matcher: matcher, CollectionMap: map[string]data.Collection{}, + itemPagerFunc: defaultItemPager, service: service, statusUpdater: statusUpdater, ctrl: ctrlOpts, @@ -266,7 +274,11 @@ func (c *Collections) Get( delta, paths, excluded, err := collectItems( ctx, - c.service, + c.itemPagerFunc( + c.service, + driveID, + "", + ), driveID, driveName, c.UpdateCollections, diff --git a/src/internal/connector/onedrive/drive.go b/src/internal/connector/onedrive/drive.go index 6270ec08a..8e1578caf 100644 --- a/src/internal/connector/onedrive/drive.go +++ b/src/internal/connector/onedrive/drive.go @@ -7,7 +7,6 @@ import ( "time" msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive" - msdrives "github.com/microsoftgraph/msgraph-sdk-go/drives" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" "github.com/pkg/errors" @@ -135,11 +134,42 @@ type itemCollector func( excluded map[string]struct{}, ) error +type itemPager interface { + GetPage(context.Context) (gapi.DeltaPageLinker, error) + SetNext(nextLink string) + ValuesIn(gapi.DeltaPageLinker) ([]models.DriveItemable, error) +} + +func defaultItemPager( + servicer graph.Servicer, + driveID, link string, +) itemPager { + return api.NewItemPager( + servicer, + driveID, + link, + []string{ + "content.downloadUrl", + "createdBy", + "createdDateTime", + "file", + "folder", + "id", + "lastModifiedDateTime", + "name", + "package", + "parentReference", + "root", + "size", + }, + ) +} + // collectItems will enumerate all items in the specified drive and hand them to the // provided `collector` method func collectItems( ctx context.Context, - service graph.Servicer, + pager itemPager, driveID, driveName string, collector itemCollector, ) (string, map[string]string, map[string]struct{}, error) { @@ -154,34 +184,8 @@ func collectItems( maps.Copy(newPaths, oldPaths) - // TODO: Specify a timestamp in the delta query - // https://docs.microsoft.com/en-us/graph/api/driveitem-delta? - // view=graph-rest-1.0&tabs=http#example-4-retrieving-delta-results-using-a-timestamp - builder := service.Client().DrivesById(driveID).Root().Delta() - pageCount := int32(999) // max we can do is 999 - requestFields := []string{ - "content.downloadUrl", - "createdBy", - "createdDateTime", - "file", - "folder", - "id", - "lastModifiedDateTime", - "name", - "package", - "parentReference", - "root", - "size", - } - requestConfig := &msdrives.ItemRootDeltaRequestBuilderGetRequestConfiguration{ - QueryParameters: &msdrives.ItemRootDeltaRequestBuilderGetQueryParameters{ - Top: &pageCount, - Select: requestFields, - }, - } - for { - r, err := builder.Get(ctx, requestConfig) + page, err := pager.GetPage(ctx) if err != nil { return "", nil, nil, errors.Wrapf( err, @@ -190,23 +194,29 @@ func collectItems( ) } - err = collector(ctx, driveID, driveName, r.GetValue(), oldPaths, newPaths, excluded) + vals, err := pager.ValuesIn(page) + if err != nil { + return "", nil, nil, errors.Wrap(err, "extracting items from response") + } + + err = collector(ctx, driveID, driveName, vals, oldPaths, newPaths, excluded) if err != nil { return "", nil, nil, err } - if r.GetOdataDeltaLink() != nil && len(*r.GetOdataDeltaLink()) > 0 { - newDeltaURL = *r.GetOdataDeltaLink() + nextLink, deltaLink := gapi.NextAndDeltaLink(page) + + if len(deltaLink) > 0 { + newDeltaURL = deltaLink } // Check if there are more items - nextLink := r.GetOdataNextLink() - if nextLink == nil { + if len(nextLink) == 0 { break } - logger.Ctx(ctx).Debugf("Found %s nextLink", *nextLink) - builder = msdrives.NewItemRootDeltaRequestBuilder(*nextLink, service.Adapter()) + logger.Ctx(ctx).Debugw("Found nextLink", "link", nextLink) + pager.SetNext(nextLink) } return newDeltaURL, newPaths, excluded, nil @@ -318,7 +328,11 @@ func GetAllFolders( for _, d := range drives { _, _, _, err = collectItems( ctx, - gs, + defaultItemPager( + gs, + *d.GetId(), + "", + ), *d.GetId(), *d.GetName(), func( diff --git a/src/internal/connector/onedrive/item_test.go b/src/internal/connector/onedrive/item_test.go index 938748ca2..6a8894ebf 100644 --- a/src/internal/connector/onedrive/item_test.go +++ b/src/internal/connector/onedrive/item_test.go @@ -115,7 +115,17 @@ func (suite *ItemIntegrationSuite) TestItemReader_oneDrive() { return nil } - _, _, _, err := collectItems(ctx, suite, suite.userDriveID, "General", itemCollector) + _, _, _, err := collectItems( + ctx, + defaultItemPager( + suite, + suite.userDriveID, + "", + ), + suite.userDriveID, + "General", + itemCollector, + ) require.NoError(suite.T(), err) // Test Requirement 2: Need a file From 24c918e99c09ae481628a5e98466e5563bc530bb Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Wed, 1 Feb 2023 10:57:38 -0800 Subject: [PATCH 30/46] Patch and test some edge cases for deserializing OneDrive metadata (#2341) ## Description Currently no folder path entry is stored for the root folder. This leads to not having a folders map but having a valid delta token if all items are in the root of the drive. Update the code to add at least an empty folders map entry so we don't think we have missing metadata. Add tests to check for these edge cases. Long-term we may want to add a folder entry for the root folder. Whether we do or not may depend on how we decide to set the state field for that collection. ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2122 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- .../connector/onedrive/collections.go | 28 +++++++--- .../connector/onedrive/collections_test.go | 54 +++++++++++++++++++ 2 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index 1e36b1ea3..e3d60fe2b 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -180,7 +180,15 @@ func deserializeMetadata( // Go through and remove partial results (i.e. path mapping but no delta URL // or vice-versa). - for k := range prevDeltas { + for k, v := range prevDeltas { + // Remove entries with an empty delta token as it's not useful. + if len(v) == 0 { + delete(prevDeltas, k) + delete(prevFolders, k) + } + + // Remove entries without a folders map as we can't tell kopia the + // hierarchy changes. if _, ok := prevFolders[k]; !ok { delete(prevDeltas, k) } @@ -287,17 +295,21 @@ func (c *Collections) Get( return nil, nil, err } + // It's alright to have an empty folders map (i.e. no folders found) but not + // an empty delta token. This is because when deserializing the metadata we + // remove entries for which there is no corresponding delta token/folder. If + // we leave empty delta tokens then we may end up setting the State field + // for collections when not actually getting delta results. if len(delta) > 0 { deltaURLs[driveID] = delta } - if len(paths) > 0 { - folderPaths[driveID] = map[string]string{} - - for id, p := range paths { - folderPaths[driveID][id] = p - } - } + // Avoid the edge case where there's no paths but we do have a valid delta + // token. We can accomplish this by adding an empty paths map for this + // drive. If we don't have this then the next backup won't use the delta + // token because it thinks the folder paths weren't persisted. + folderPaths[driveID] = map[string]string{} + maps.Copy(folderPaths[driveID], paths) maps.Copy(excludedItems, excluded) } diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index c250afe2a..39e094cd6 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -711,6 +711,60 @@ func (suite *OneDriveCollectionsSuite) TestDeserializeMetadata() { expectedPaths: map[string]map[string]string{}, errCheck: assert.NoError, }, + { + // An empty path map but valid delta results in metadata being returned + // since it's possible to have a drive with no folders other than the + // root. + name: "EmptyPaths", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{driveID1: deltaURL1}, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: {}, + }, + ), + } + }, + }, + expectedDeltas: map[string]string{driveID1: deltaURL1}, + expectedPaths: map[string]map[string]string{driveID1: {}}, + errCheck: assert.NoError, + }, + { + // An empty delta map but valid path results in no metadata for that drive + // being returned since the path map is only useful if we have a valid + // delta. + name: "EmptyDeltas", + cols: []func() []graph.MetadataCollectionEntry{ + func() []graph.MetadataCollectionEntry { + return []graph.MetadataCollectionEntry{ + graph.NewMetadataEntry( + graph.DeltaURLsFileName, + map[string]string{ + driveID1: "", + }, + ), + graph.NewMetadataEntry( + graph.PreviousPathFileName, + map[string]map[string]string{ + driveID1: { + folderID1: path1, + }, + }, + ), + } + }, + }, + expectedDeltas: map[string]string{}, + expectedPaths: map[string]map[string]string{}, + errCheck: assert.NoError, + }, { name: "SuccessTwoDrivesTwoCollections", cols: []func() []graph.MetadataCollectionEntry{ From 93e8b67d156993281fe061966520c3a4a140ef58 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 1 Feb 2023 16:07:14 -0500 Subject: [PATCH 31/46] GC: Backup: ItemAttachment Complete (#2358) ## Description Bug fix: Ensures that `item.Attachment` content is completely backed up. - Exchange Changes affect the behavior of Mail and Events. Downloads can be potentially bigger if the user have these types of attachments. - `item.Attachments` are specific to Outlook objects such as `Events` or `Messages` are included as an attachment rather than inline. ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included ## Type of change - [x] :bug: Bugfix ## Issue(s) * related to #2353 ## Test Plan - [x] :muscle: Manual --- src/internal/connector/exchange/api/events.go | 11 +++++++++-- src/internal/connector/exchange/api/mail.go | 7 ++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index 962b2e576..810c1a2ff 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -108,7 +108,14 @@ func (c Events) GetItem( return nil, nil, err } - var errs *multierror.Error + var ( + errs *multierror.Error + options = &users.ItemEventsItemAttachmentsRequestBuilderGetRequestConfiguration{ + QueryParameters: &users.ItemEventsItemAttachmentsRequestBuilderGetQueryParameters{ + Expand: []string{"microsoft.graph.itemattachment/item"}, + }, + } + ) if *event.GetHasAttachments() || HasAttachments(event.GetBody()) { for count := 0; count < numberOfRetries; count++ { @@ -117,7 +124,7 @@ func (c Events) GetItem( UsersById(user). EventsById(itemID). Attachments(). - Get(ctx, nil) + Get(ctx, options) if err == nil { event.SetAttachments(attached.GetValue()) break diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index 29c03b82f..6a863322e 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -130,13 +130,18 @@ func (c Mail) GetItem( var errs *multierror.Error if *mail.GetHasAttachments() || HasAttachments(mail.GetBody()) { + options := &users.ItemMessagesItemAttachmentsRequestBuilderGetRequestConfiguration{ + QueryParameters: &users.ItemMessagesItemAttachmentsRequestBuilderGetQueryParameters{ + Expand: []string{"microsoft.graph.itemattachment/item"}, + }, + } for count := 0; count < numberOfRetries; count++ { attached, err := c.largeItem. Client(). UsersById(user). MessagesById(itemID). Attachments(). - Get(ctx, nil) + Get(ctx, options) if err == nil { mail.SetAttachments(attached.GetValue()) break From cf1c80271f7a3416db7fb83febbc0c29bcc6662f Mon Sep 17 00:00:00 2001 From: ashmrtn Date: Wed, 1 Feb 2023 13:34:16 -0800 Subject: [PATCH 32/46] OneDrive tests for Get() (#2342) ## Description Create a test case that allows checking most of the Get call (getting item data is not mocked right now). This allows better checking on things like: * having multiple drives to backup * having items split across multiple delta query pages * errors returned by the API * aggregation of delta URLs and folder paths in metadata Eventually these tests could help with checking: * consumption of metadata for incremental backups * consumption of delta tokens for incremental backups Long-term, we may want to merge these with the UpdateCollections tests as there is overlap between the two. We may also want to consider a more stable API for checking the returned items. Right now it partly relies on being able to cast to a onedrive.Collection struct instead of using `Items()` ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [x] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * #2264 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- .../connector/onedrive/collections.go | 29 +- .../connector/onedrive/collections_test.go | 423 +++++++++++++++++- 2 files changed, 439 insertions(+), 13 deletions(-) diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index e3d60fe2b..200e51e23 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -67,6 +67,12 @@ type Collections struct { // Not the most ideal, but allows us to change the pager function for testing // as needed. This will allow us to mock out some scenarios during testing. + drivePagerFunc func( + source driveSource, + servicer graph.Servicer, + resourceOwner string, + fields []string, + ) (drivePager, error) itemPagerFunc func( servicer graph.Servicer, driveID, link string, @@ -89,16 +95,17 @@ func NewCollections( ctrlOpts control.Options, ) *Collections { return &Collections{ - itemClient: itemClient, - tenant: tenant, - resourceOwner: resourceOwner, - source: source, - matcher: matcher, - CollectionMap: map[string]data.Collection{}, - itemPagerFunc: defaultItemPager, - service: service, - statusUpdater: statusUpdater, - ctrl: ctrlOpts, + itemClient: itemClient, + tenant: tenant, + resourceOwner: resourceOwner, + source: source, + matcher: matcher, + CollectionMap: map[string]data.Collection{}, + drivePagerFunc: PagerForSource, + itemPagerFunc: defaultItemPager, + service: service, + statusUpdater: statusUpdater, + ctrl: ctrlOpts, } } @@ -250,7 +257,7 @@ func (c *Collections) Get( } // Enumerate drives for the specified resourceOwner - pager, err := PagerForSource(c.source, c.service, c.resourceOwner, nil) + pager, err := c.drivePagerFunc(c.source, c.service, c.resourceOwner, nil) if err != nil { return nil, nil, err } diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index 39e094cd6..21dae9549 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -1,9 +1,11 @@ package onedrive import ( + "context" "strings" "testing" + "github.com/google/uuid" "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -11,6 +13,7 @@ import ( "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/connector/graph" + gapi "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" @@ -969,7 +972,419 @@ func (suite *OneDriveCollectionsSuite) TestDeserializeMetadata() { } } -func driveItem(id string, name string, parentPath string, isFile, isFolder, isPackage bool) models.DriveItemable { +type mockDeltaPageLinker struct { + link *string + delta *string +} + +func (pl *mockDeltaPageLinker) GetOdataNextLink() *string { + return pl.link +} + +func (pl *mockDeltaPageLinker) GetOdataDeltaLink() *string { + return pl.delta +} + +type deltaPagerResult struct { + items []models.DriveItemable + nextLink *string + deltaLink *string + err error +} + +type mockItemPager struct { + // DriveID -> set of return values for queries for that drive. + toReturn []deltaPagerResult + getIdx int +} + +func (p *mockItemPager) GetPage(context.Context) (gapi.DeltaPageLinker, error) { + if len(p.toReturn) <= p.getIdx { + return nil, assert.AnError + } + + idx := p.getIdx + p.getIdx++ + + return &mockDeltaPageLinker{ + p.toReturn[idx].nextLink, + p.toReturn[idx].deltaLink, + }, p.toReturn[idx].err +} + +func (p *mockItemPager) SetNext(string) {} + +func (p *mockItemPager) ValuesIn(gapi.DeltaPageLinker) ([]models.DriveItemable, error) { + idx := p.getIdx + if idx > 0 { + // Return values lag by one since we increment in GetPage(). + idx-- + } + + if len(p.toReturn) <= idx { + return nil, assert.AnError + } + + return p.toReturn[idx].items, nil +} + +func (suite *OneDriveCollectionsSuite) TestGet() { + anyFolder := (&selectors.OneDriveBackup{}).Folders(selectors.Any())[0] + + tenant := "a-tenant" + user := "a-user" + + metadataPath, err := path.Builder{}.ToServiceCategoryMetadataPath( + tenant, + user, + path.OneDriveService, + path.FilesCategory, + false, + ) + require.NoError(suite.T(), err, "making metadata path") + + folderPath := expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath+"/folder", + )[0] + + empty := "" + next := "next" + delta := "delta1" + delta2 := "delta2" + + driveID1 := uuid.NewString() + drive1 := models.NewDrive() + drive1.SetId(&driveID1) + drive1.SetName(&driveID1) + + driveID2 := uuid.NewString() + drive2 := models.NewDrive() + drive2.SetId(&driveID2) + drive2.SetName(&driveID2) + + driveBasePath2 := "drive/driveID2/root:" + + folderPath2 := expectedPathAsSlice( + suite.T(), + tenant, + user, + driveBasePath2+"/folder", + )[0] + + table := []struct { + name string + drives []models.Driveable + items map[string][]deltaPagerResult + errCheck assert.ErrorAssertionFunc + // Collection name -> set of item IDs. We can't check item data because + // that's not mocked out. Metadata is checked separately. + expectedCollections map[string][]string + expectedDeltaURLs map[string]string + expectedFolderPaths map[string]map[string]string + expectedDelList map[string]struct{} + }{ + { + name: "OneDrive_OneItemPage_DelFileOnly_NoFolders_NoErrors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + delItem("file", testBaseDrivePath, true, false, false), + }, + deltaLink: &delta, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{}, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + }, + expectedFolderPaths: map[string]map[string]string{ + // We need an empty map here so deserializing metadata knows the delta + // token for this drive is valid. + driveID1: {}, + }, + expectedDelList: map[string]struct{}{ + "file": {}, + }, + }, + { + name: "OneDrive_OneItemPage_NoFolders_NoErrors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveItem("file", "file", testBaseDrivePath, true, false, false), + }, + deltaLink: &delta, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{ + expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + )[0]: {"file"}, + }, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + }, + expectedFolderPaths: map[string]map[string]string{ + // We need an empty map here so deserializing metadata knows the delta + // token for this drive is valid. + driveID1: {}, + }, + expectedDelList: map[string]struct{}{}, + }, + { + name: "OneDrive_OneItemPage_NoErrors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveItem("folder", "folder", testBaseDrivePath, false, true, false), + driveItem("file", "file", testBaseDrivePath+"/folder", true, false, false), + }, + deltaLink: &delta, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{ + folderPath: {"file"}, + }, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + }, + expectedFolderPaths: map[string]map[string]string{ + driveID1: { + "folder": folderPath, + }, + }, + expectedDelList: map[string]struct{}{}, + }, + { + name: "OneDrive_OneItemPage_EmptyDelta_NoErrors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveItem("folder", "folder", testBaseDrivePath, false, true, false), + driveItem("file", "file", testBaseDrivePath+"/folder", true, false, false), + }, + deltaLink: &empty, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{ + folderPath: {"file"}, + }, + expectedDeltaURLs: map[string]string{}, + expectedFolderPaths: map[string]map[string]string{}, + expectedDelList: map[string]struct{}{}, + }, + { + name: "OneDrive_TwoItemPages_NoErrors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveItem("folder", "folder", testBaseDrivePath, false, true, false), + driveItem("file", "file", testBaseDrivePath+"/folder", true, false, false), + }, + nextLink: &next, + }, + { + items: []models.DriveItemable{ + driveItem("folder", "folder", testBaseDrivePath, false, true, false), + driveItem("file2", "file2", testBaseDrivePath+"/folder", true, false, false), + }, + deltaLink: &delta, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{ + folderPath: {"file", "file2"}, + }, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + }, + expectedFolderPaths: map[string]map[string]string{ + driveID1: { + "folder": folderPath, + }, + }, + expectedDelList: map[string]struct{}{}, + }, + { + name: "TwoDrives_OneItemPageEach_NoErrors", + drives: []models.Driveable{ + drive1, + drive2, + }, + items: map[string][]deltaPagerResult{ + driveID1: { + { + items: []models.DriveItemable{ + driveItem("folder", "folder", testBaseDrivePath, false, true, false), + driveItem("file", "file", testBaseDrivePath+"/folder", true, false, false), + }, + deltaLink: &delta, + }, + }, + driveID2: { + { + items: []models.DriveItemable{ + driveItem("folder", "folder", driveBasePath2, false, true, false), + driveItem("file", "file", driveBasePath2+"/folder", true, false, false), + }, + deltaLink: &delta2, + }, + }, + }, + errCheck: assert.NoError, + expectedCollections: map[string][]string{ + folderPath: {"file"}, + folderPath2: {"file"}, + }, + expectedDeltaURLs: map[string]string{ + driveID1: delta, + driveID2: delta2, + }, + expectedFolderPaths: map[string]map[string]string{ + driveID1: { + "folder": folderPath, + }, + driveID2: { + "folder": folderPath2, + }, + }, + expectedDelList: map[string]struct{}{}, + }, + { + name: "OneDrive_OneItemPage_Errors", + drives: []models.Driveable{drive1}, + items: map[string][]deltaPagerResult{ + driveID1: { + { + err: assert.AnError, + }, + }, + }, + errCheck: assert.Error, + expectedCollections: nil, + expectedDeltaURLs: nil, + expectedFolderPaths: nil, + expectedDelList: nil, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + ctx, flush := tester.NewContext() + defer flush() + + drivePagerFunc := func( + source driveSource, + servicer graph.Servicer, + resourceOwner string, + fields []string, + ) (drivePager, error) { + return &mockDrivePager{ + toReturn: []pagerResult{ + { + drives: test.drives, + }, + }, + }, nil + } + + itemPagerFunc := func( + servicer graph.Servicer, + driveID, link string, + ) itemPager { + return &mockItemPager{ + toReturn: test.items[driveID], + } + } + + c := NewCollections( + graph.HTTPClient(graph.NoTimeout()), + tenant, + user, + OneDriveSource, + testFolderMatcher{anyFolder}, + &MockGraphService{}, + func(*support.ConnectorOperationStatus) {}, + control.Options{}, + ) + c.drivePagerFunc = drivePagerFunc + c.itemPagerFunc = itemPagerFunc + + // TODO(ashmrtn): Allow passing previous metadata. + cols, _, err := c.Get(ctx, nil) + test.errCheck(t, err) + + if err != nil { + return + } + + for _, baseCol := range cols { + folderPath := baseCol.FullPath().String() + if folderPath == metadataPath.String() { + deltas, paths, err := deserializeMetadata(ctx, []data.Collection{baseCol}) + if !assert.NoError(t, err, "deserializing metadata") { + continue + } + + assert.Equal(t, test.expectedDeltaURLs, deltas) + assert.Equal(t, test.expectedFolderPaths, paths) + + continue + } + + // TODO(ashmrtn): We should really be getting items in the collection + // via the Items() channel, but we don't have a way to mock out the + // actual item fetch yet (mostly wiring issues). The lack of that makes + // this check a bit more bittle since internal details can change. + col, ok := baseCol.(*Collection) + require.True(t, ok, "getting onedrive.Collection handle") + + itemIDs := make([]string, 0, len(col.driveItems)) + + for id := range col.driveItems { + itemIDs = append(itemIDs, id) + } + + assert.ElementsMatch(t, test.expectedCollections[folderPath], itemIDs) + } + + // TODO(ashmrtn): Uncomment this when we begin return the set of items to + // remove from the upcoming backup. + // assert.Equal(t, test.expectedDelList, delList) + }) + } +} + +func driveItem( + id string, + name string, + parentPath string, + isFile, isFolder, isPackage bool, +) models.DriveItemable { item := models.NewDriveItem() item.SetName(&name) item.SetId(&id) @@ -992,7 +1407,11 @@ func driveItem(id string, name string, parentPath string, isFile, isFolder, isPa // delItem creates a DriveItemable that is marked as deleted. path must be set // to the base drive path. -func delItem(id string, parentPath string, isFile, isFolder, isPackage bool) models.DriveItemable { +func delItem( + id string, + parentPath string, + isFile, isFolder, isPackage bool, +) models.DriveItemable { item := models.NewDriveItem() item.SetId(&id) item.SetDeleted(models.NewDeleted()) From 46d584ec7502b6727e419d44cac22ba444cefb80 Mon Sep 17 00:00:00 2001 From: Danny Date: Wed, 1 Feb 2023 18:29:33 -0500 Subject: [PATCH 33/46] GC: Restore: Disable Item.Attachment Restore (#2359) ## Description `Item.Attachments` have unique properties that require transformation to be uploaded into the M365 system. All objects of this type are temporarily prevented from uploading via this patch. As Issue #2353 is addressed, there will be more coverage for the various types of item attachments. ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included ## Type of change - [x] :sunflower: Feature ## Issue(s) *related to #2353 ## Test Plan - [x] :muscle: Manual --- CHANGELOG.md | 2 +- src/internal/connector/exchange/attachment.go | 11 +++++++++ .../connector/exchange/restore_test.go | 14 ++++++++++- .../mockconnector/mock_data_message.go | 23 +++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3333c7ad6..50a5f3270 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Check if the user specified for an exchange backup operation has a mailbox. ### Changed - +- Item.Attachments are disabled from being restored for the patching of ([#2353](https://github.com/alcionai/corso/issues/2353)) - BetaClient introduced. Enables Corso to be able to interact with SharePoint Page objects. Package located `/internal/connector/graph/betasdk` - Handle case where user's drive has not been initialized - Inline attachments (e.g. copy/paste ) are discovered and backed up correctly ([#2163](https://github.com/alcionai/corso/issues/2163)) diff --git a/src/internal/connector/exchange/attachment.go b/src/internal/connector/exchange/attachment.go index 5cbce271c..5b6334e21 100644 --- a/src/internal/connector/exchange/attachment.go +++ b/src/internal/connector/exchange/attachment.go @@ -53,6 +53,17 @@ func uploadAttachment( return nil } + // item Attachments to be skipped until the completion of Issue #2353 + if attachmentType == models.ITEM_ATTACHMENTTYPE { + logger.Ctx(ctx).Infow("item attachment uploads are not supported ", + "attachment_name", *attachment.GetName(), // TODO: Update to support PII protection + "attachment_type", attachmentType, + "attachment_id", *attachment.GetId(), + ) + + return nil + } + // For Item/Reference attachments *or* file attachments < 3MB, use the attachments endpoint if attachmentType != models.FILE_ATTACHMENTTYPE || *attachment.GetSize() < largeAttachmentSize { err := uploader.uploadSmallAttachment(ctx, attachment) diff --git a/src/internal/connector/exchange/restore_test.go b/src/internal/connector/exchange/restore_test.go index 9c32fd530..187d0c127 100644 --- a/src/internal/connector/exchange/restore_test.go +++ b/src/internal/connector/exchange/restore_test.go @@ -175,6 +175,18 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { return *folder.GetId() }, }, + { + name: "Test Mail: Item Attachment", + bytes: mockconnector.GetMockMessageWithItemAttachmentEvent("Event Item Attachment"), + category: path.EmailCategory, + destination: func(t *testing.T, ctx context.Context) string { + folderName := "TestRestoreMailItemAttachment: " + common.FormatSimpleDateTime(now) + folder, err := suite.ac.Mail().CreateMailFolder(ctx, userID, folderName) + require.NoError(t, err) + + return *folder.GetId() + }, + }, { name: "Test Mail: One Large Attachment", bytes: mockconnector.GetMockMessageWithLargeAttachment("Restore Large Attachment"), @@ -266,7 +278,7 @@ func (suite *ExchangeRestoreSuite) TestRestoreExchangeObject() { userID, ) assert.NoError(t, err, support.ConnectorStackErrorTrace(err)) - assert.NotNil(t, info, "item info is populated") + assert.NotNil(t, info, "item info was not populated") assert.NoError(t, deleters[test.category].DeleteContainer(ctx, userID, destination)) }) } diff --git a/src/internal/connector/mockconnector/mock_data_message.go b/src/internal/connector/mockconnector/mock_data_message.go index 597447492..da06cbaab 100644 --- a/src/internal/connector/mockconnector/mock_data_message.go +++ b/src/internal/connector/mockconnector/mock_data_message.go @@ -336,3 +336,26 @@ func GetMockEventMessageRequest(subject string) []byte { return []byte(message) } + +func GetMockMessageWithItemAttachmentEvent(subject string) []byte { + //nolint:lll + message := "{\"id\":\"AAMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOABGAAAAAAAPvVwUramXT7jlSGpVU8_7BwB8wYc0thTTTYl3RpEYIUq_AAAAAAEMAAB8wYc0thTTTYl3RpEYIUq_AADFfThMAAA=\",\"@odata.type\":\"#microsoft.graph.message\"," + + "\"@odata.etag\":\"W/\\\"CQAAABYAAAB8wYc0thTTTYl3RpEYIUq+AADFK3BH\\\"\",\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('dustina%408qzvrj.onmicrosoft.com')/messages/$entity\",\"categories\":[]," + + "\"changeKey\":\"CQAAABYAAAB8wYc0thTTTYl3RpEYIUq+AADFK3BH\",\"createdDateTime\":\"2023-02-01T13:48:43Z\",\"lastModifiedDateTime\":\"2023-02-01T18:27:03Z\"," + + "\"attachments\":[{\"id\":\"AAMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOABGAAAAAAAPvVwUramXT7jlSGpVU8_7BwB8wYc0thTTTYl3RpEYIUq_AAAAAAEMAAB8wYc0thTTTYl3RpEYIUq_AADFfThMAAABEgAQAKHxTL6mNCZPo71dbwrfKYM=\"," + + "\"@odata.type\":\"#microsoft.graph.itemAttachment\",\"isInline\":false,\"lastModifiedDateTime\":\"2023-02-01T13:52:56Z\",\"name\":\"Holidayevent\",\"size\":2059,\"item\":{\"id\":\"\",\"@odata.type\":\"#microsoft.graph.event\"," + + "\"createdDateTime\":\"2023-02-01T13:52:56Z\",\"lastModifiedDateTime\":\"2023-02-01T13:52:56Z\",\"body\":{\"content\":\"\\r\\nLet'slookforfunding!\"," + + "\"contentType\":\"html\"},\"end\":{\"dateTime\":\"2016-12-02T19:00:00.0000000Z\",\"timeZone\":\"UTC\"}," + + "\"hasAttachments\":false,\"isAllDay\":false,\"isCancelled\":false,\"isDraft\":true,\"isOnlineMeeting\":false,\"isOrganizer\":true,\"isReminderOn\":false,\"organizer\":{\"emailAddress\":{\"address\":\"" + defaultMessageFrom + "\",\"name\":\"" + defaultAlias + "\"}}," + + "\"originalEndTimeZone\":\"tzone://Microsoft/Utc\",\"originalStartTimeZone\":\"tzone://Microsoft/Utc\",\"reminderMinutesBeforeStart\":0,\"responseRequested\":true,\"start\":{\"dateTime\":\"2016-12-02T18:00:00.0000000Z\",\"timeZone\":\"UTC\"}," + + "\"subject\":\"Discussgiftsforchildren\",\"type\":\"singleInstance\"}}],\"bccRecipients\":[],\"body\":{\"content\":\"\\r\\n\\r\\n\\r\\nLookingtodothis \",\"contentType\":\"html\"}," + + "\"bodyPreview\":\"Lookingtodothis\",\"ccRecipients\":[],\"conversationId\":\"AAQkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOAAQADGvj5ACBMdGpESX4xSOxCo=\",\"conversationIndex\":\"AQHZNkPmMa+PkAIEx0akRJfjFI7EKg==\",\"flag\":{\"flagStatus\":\"notFlagged\"}," + + "\"from\":{\"emailAddress\":{\"address\":\"" + defaultMessageFrom + "\",\"name\":\"" + defaultAlias + "\"}},\"hasAttachments\":true,\"importance\":\"normal\",\"inferenceClassification\":\"focused\"," + + "\"internetMessageId\":\"\",\"isDeliveryReceiptRequested\":false,\"isDraft\":false,\"isRead\":true,\"isReadReceiptRequested\":false," + + "\"parentFolderId\":\"AQMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4ADVkZWQwNmNlMTgALgAAAw_9XBStqZdPuOVIalVTz7sBAHzBhzS2FNNNiXdGkRghSr4AAAIBDAAAAA==\",\"receivedDateTime\":\"2023-02-01T13:48:47Z\",\"replyTo\":[]," + + "\"sender\":{\"emailAddress\":{\"address\":\"" + defaultMessageSender + "\",\"name\":\"" + defaultAlias + "\"}},\"sentDateTime\":\"2023-02-01T13:48:46Z\"," + + "\"subject\":\"" + subject + "\",\"toRecipients\":[{\"emailAddress\":{\"address\":\"" + defaultMessageTo + "\",\"name\":\"" + defaultAlias + "\"}}]," + + "\"webLink\":\"https://outlook.office365.com/owa/?ItemID=AAMkAGQ1NzViZTdhLTEwMTMtNGJjNi05YWI2LTg4NWRlZDA2Y2UxOABGAAAAAAAPvVwUramXT7jlSGpVU8%2B7BwB8wYc0thTTTYl3RpEYIUq%2BAAAAAAEMAAB8wYc0thTTTYl3RpEYIUq%2BAADFfThMAAA%3D&exvsurl=1&viewmodel=ReadMessageItem\"}" + + return []byte(message) +} From 2a0640f9f371f0347c6b822ac0eb83df7a84bba2 Mon Sep 17 00:00:00 2001 From: Vaibhav Kamra Date: Wed, 1 Feb 2023 19:35:34 -0800 Subject: [PATCH 34/46] [chore] Remove redundant module use (#2363) ## Description Standardize on `go-humanize` ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #1038 ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- src/go.mod | 1 - src/go.sum | 2 -- src/internal/connector/support/status.go | 4 ++-- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/go.mod b/src/go.mod index f947ed45a..481fd7fe2 100644 --- a/src/go.mod +++ b/src/go.mod @@ -71,7 +71,6 @@ require ( github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect - github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.15.12 // indirect diff --git a/src/go.sum b/src/go.sum index 02f39a5a5..0befca54a 100644 --- a/src/go.sum +++ b/src/go.sum @@ -209,8 +209,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= -github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= diff --git a/src/internal/connector/support/status.go b/src/internal/connector/support/status.go index 3f2435263..7e38758d3 100644 --- a/src/internal/connector/support/status.go +++ b/src/internal/connector/support/status.go @@ -4,8 +4,8 @@ import ( "context" "fmt" + "github.com/dustin/go-humanize" multierror "github.com/hashicorp/go-multierror" - bytesize "github.com/inhies/go-bytesize" "github.com/alcionai/corso/src/pkg/logger" ) @@ -142,7 +142,7 @@ func (cos *ConnectorOperationStatus) String() string { cos.lastOperation.String(), cos.Successful, cos.ObjectCount, - bytesize.New(float64(cos.bytes)), + humanize.Bytes(uint64(cos.bytes)), cos.FolderCount, ) From 19dbad4c460d947cbeb78315240a21f95aff20f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 03:45:51 +0000 Subject: [PATCH 35/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20http-cache-se?= =?UTF-8?q?mantics=20from=204.1.0=20to=204.1.1=20in=20/website=20(#2367)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/package-lock.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/website/package-lock.json b/website/package-lock.json index 100008906..632819d60 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -7999,10 +7999,9 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "license": "BSD-2-Clause" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http-deceiver": { "version": "1.2.7", @@ -19958,9 +19957,9 @@ } }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "http-deceiver": { "version": "1.2.7", From 6f44a507b57d9f616465884a1f830043fa031d3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 06:38:11 +0000 Subject: [PATCH 36/46] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Bump=20github.com/aw?= =?UTF-8?q?s/aws-sdk-go=20from=201.44.191=20to=201.44.192=20in=20/src=20(#?= =?UTF-8?q?2369)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) from 1.44.191 to 1.44.192.
Release notes

Sourced from github.com/aws/aws-sdk-go's releases.

Release v1.44.192 (2023-02-01)

Service Client Updates

  • service/devops-guru: Updates service API and documentation
  • service/forecast: Updates service API and documentation
  • service/iam: Updates service documentation
    • Documentation updates for AWS Identity and Access Management (IAM).
  • service/mediatailor: Updates service API and documentation
  • service/sns: Updates service documentation
    • Additional attributes added for set-topic-attributes.
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/aws/aws-sdk-go&package-manager=go_modules&previous-version=1.44.191&new-version=1.44.192)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) You can trigger a rebase of this PR by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
--- src/go.mod | 2 +- src/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/go.mod b/src/go.mod index 481fd7fe2..bf43f6494 100644 --- a/src/go.mod +++ b/src/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 github.com/alcionai/clues v0.0.0-20230131232239-cee86233b005 - github.com/aws/aws-sdk-go v1.44.191 + github.com/aws/aws-sdk-go v1.44.192 github.com/aws/aws-xray-sdk-go v1.8.0 github.com/google/uuid v1.3.0 github.com/hashicorp/go-multierror v1.1.1 diff --git a/src/go.sum b/src/go.sum index 0befca54a..17d6f25bd 100644 --- a/src/go.sum +++ b/src/go.sum @@ -62,8 +62,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5 github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/aws/aws-sdk-go v1.44.191 h1:GnbkalCx/AgobaorDMFCa248acmk+91+aHBQOk7ljzU= -github.com/aws/aws-sdk-go v1.44.191/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/aws/aws-sdk-go v1.44.192 h1:KL54vCxRd5v5XBGjnF3FelzXXwl+aWHDmDTihFmRNgM= +github.com/aws/aws-sdk-go v1.44.192/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/aws/aws-xray-sdk-go v1.8.0 h1:0xncHZ588wB/geLjbM/esoW3FOEThWy2TJyb4VXfLFY= github.com/aws/aws-xray-sdk-go v1.8.0/go.mod h1:7LKe47H+j3evfvS1+q0wzpoaGXGrF3mUsfM+thqVO+A= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= From 844dcae9b6c351e8688f7e9536c400053b313ae6 Mon Sep 17 00:00:00 2001 From: Danny Date: Thu, 2 Feb 2023 12:22:04 -0500 Subject: [PATCH 37/46] GC: Logging: Expand `ItemAttachment` object details for coverage (#2366) ## Description Updates within /internal/connector/exchange package. Adds helper function for displaying the internal object item type that is included as an attachment. This will help overall with supporting additional `ItemAttachment` objects. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * related to #2353 ## Test Plan - [x] :muscle: Manual --- src/internal/connector/exchange/attachment.go | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/internal/connector/exchange/attachment.go b/src/internal/connector/exchange/attachment.go index 5b6334e21..6ed05b5df 100644 --- a/src/internal/connector/exchange/attachment.go +++ b/src/internal/connector/exchange/attachment.go @@ -55,9 +55,15 @@ func uploadAttachment( // item Attachments to be skipped until the completion of Issue #2353 if attachmentType == models.ITEM_ATTACHMENTTYPE { + name := "" + if attachment.GetName() != nil { + name = *attachment.GetName() + } + logger.Ctx(ctx).Infow("item attachment uploads are not supported ", - "attachment_name", *attachment.GetName(), // TODO: Update to support PII protection + "attachment_name", name, // TODO: Update to support PII protection "attachment_type", attachmentType, + "internal_item_type", getItemAttachmentItemType(attachment), "attachment_id", *attachment.GetId(), ) @@ -101,3 +107,19 @@ func uploadLargeAttachment(ctx context.Context, uploader attachmentUploadable, return nil } + +func getItemAttachmentItemType(query models.Attachmentable) string { + empty := "" + attachment, ok := query.(models.ItemAttachmentable) + + if !ok { + return empty + } + + item := attachment.GetItem() + if item.GetOdataType() == nil { + return empty + } + + return *item.GetOdataType() +} From a3aa3bcad03a84a05b092a3f72ac20fc2e118819 Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 2 Feb 2023 13:34:53 -0700 Subject: [PATCH 38/46] Issue 2329 logfile 1 (#2344) ## Description Prevents showing the log file location when calling commands that defer to help output or env details. Certain cases aren't caught, such as when calling a command with no flags (ex: `corso backup create exchange`). In those cases we catch the lack of flags and manually display the usage within the command handler. ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Issue(s) * #2329 ## Test Plan - [x] :muscle: Manual --- .gitignore | 3 +++ src/cli/cli.go | 8 ++++++++ src/pkg/logger/logger.go | 3 +++ 3 files changed, 14 insertions(+) diff --git a/.gitignore b/.gitignore index 46f5189b8..911d91a10 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ .corso_test.toml .corso.toml +# Logging +.corso.log + # Build directories /bin /docker/bin diff --git a/src/cli/cli.go b/src/cli/cli.go index f202e4953..77a03b1a7 100644 --- a/src/cli/cli.go +++ b/src/cli/cli.go @@ -8,6 +8,7 @@ import ( "github.com/alcionai/clues" "github.com/spf13/cobra" + "golang.org/x/exp/slices" "github.com/alcionai/corso/src/cli/backup" "github.com/alcionai/corso/src/cli/config" @@ -51,6 +52,13 @@ func preRun(cc *cobra.Command, args []string) error { flagSl = append(flagSl, f) } + avoidTheseCommands := []string{ + "corso", "env", "help", "backup", "details", "list", "restore", "delete", "repo", "init", "connect", + } + if len(logger.LogFile) > 0 && !slices.Contains(avoidTheseCommands, cc.Use) { + print.Info(cc.Context(), "Logging to file: "+logger.LogFile) + } + log.Infow("cli command", "command", cc.CommandPath(), "flags", flagSl, "version", version.CurrentVersion()) return nil diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index 923af44c8..abbce9119 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -29,6 +29,8 @@ var ( DebugAPI bool readableOutput bool + + LogFile string ) type logLevel int @@ -118,6 +120,7 @@ func PreloadLoggingFlags() (string, string) { } if logfile != "stdout" && logfile != "stderr" { + LogFile = logfile logdir := filepath.Dir(logfile) print.Info(context.Background(), "Logging to file: "+logfile) From b00b41a6bda909faf85d65e58ab98443aa3284c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?No=C4=8Dnica=20Mellifera?= Date: Thu, 2 Feb 2023 17:12:18 -0800 Subject: [PATCH 39/46] new blog post on storage options for corso (#2362) ## Description new blog post on storage options for corso ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [x] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * # ## Test Plan - [x] :muscle: Manual - [ ] :zap: Unit test - [ ] :green_heart: E2E --- website/blog/2023-2-4-where-to-store-corso.md | 118 ++++++++++++++++++ website/blog/images/boxes_web.jpeg | Bin 0 -> 354646 bytes website/styles/Vocab/Base/accept.txt | 4 +- 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 website/blog/2023-2-4-where-to-store-corso.md create mode 100644 website/blog/images/boxes_web.jpeg diff --git a/website/blog/2023-2-4-where-to-store-corso.md b/website/blog/2023-2-4-where-to-store-corso.md new file mode 100644 index 000000000..cdde919b4 --- /dev/null +++ b/website/blog/2023-2-4-where-to-store-corso.md @@ -0,0 +1,118 @@ +--- +slug: where-to-store-corso +title: "Where to store your Corso Repository" +description: "Storage Options for Corso" +authors: nica +tags: [corso, microsoft 365, backups, S3] +date: 2023-2-4 +image: ./images/boxes_web.jpeg +--- + +![image of a large number of packing boxes](./images/boxes_web.jpeg) + +We all know that Corso is a free and open-source tool for creating backups of your Microsoft 365 data. But where does +that data go? + +Corso creates a repository to store your backups, and the default in our documentation is to send that data to AWS S3. +It's possible however to back up to any object storage system that has an S3-compatible API. Let’s talk about some options. + + +## S3-Compatible Object Storage + +A number of other cloud providers aren’t the 500-pound gorilla of AWS but still offer an S3-compatible API. +Some of them include: + +- Google Cloud: One of the largest cloud providers in the world, Google offers +[an S3-compatible API](https://cloud.google.com/storage/docs/interoperability) on top of its Google Cloud Storage (GCS) offering. +- Backblaze: Known for its deep analysis of hard drive failure statistics, Backblaze offers an S3-compatible API for its +B2 Cloud Storage product. They also make the bold claim of costing [significantly less than AWS S3](https://www.backblaze.com/b2/cloud-storage-pricing.html) +(I haven’t evaluated these claims) but Glacier is still cheaper (see below for more details) +- HPE: HPE Greenlake offers S3 compatibility and claims superior performance over S3. If you want to get a sense of how +‘Enterprise’ HPE is, the best writeup I could find of their offerings is [available only as a PDF](https://www.hpe.com/us/en/collaterals/collateral.a50006216.Create-value-from-data-2C-at-scale-E2-80-93-HPE-GreenLake-for-Scality-solution-brief.html). +- Wasabi: Another popular offering, Wasabi has great integration with existing AWS components at a reduced +cost but watch out for the minimum monthly storage charge and the minimum storage duration policy! + +This is an incomplete list, but any S3-compliant storage with immediate retrieval is expected to work with Corso today. + +## Local S3 Testing + +In my own testing, I use [MinIO](https://min.io/) to create a local S3 server and bucket. This has some great advantages +including extremely low latency for testing. Unless you have a significant hardware and software investment to ensure +reliable storage and compute infrastructure, you probably don't want to rely on a MinIO setup as your primary +backup location, but it’s a great way to do a zero-cost test backup that you totally control. + +While there are a number of in-depth tutorials on how to use +[MinIO to run a local S3 server](https://simonjcarr.medium.com/running-s3-object-storage-locally-with-minio-f50540ffc239), +here’s the single script that can run a non-production instance of MinIO within a Docker container (you’ll need Docker +and the AWS CLI as prerequisites) and get you started with Corso quickly: + +```bash +mkdir -p ~\s/minio/data + +docker run \ + -p 9000:9000 \ + -p 9090:9090 \ + --name minio \ + -v ~/minio/data:/data \ + -e "MINIO_ROOT_USER=ROOTNAME" \ + -e "MINIO_ROOT_PASSWORD=CHANGEME123" \ + quay.io/minio/minio server /data --console-address ":9090" +``` + +In a separate window, create a bucket (`corso-backup`) for use with Corso. + +```bash +export AWS_ACCESS_KEY_ID=ROOTNAME +export AWS_SECRET_ACCESS_KEY=CHANGEME123 + +aws s3api create-bucket --bucket corso-backup --endpoint=http://127.0.0.1:9000 +``` + +To connect Corso to a local MinIO server with `[corso repo init](https://corsobackup.io/docs/cli/corso-repo-init-s3/)` +you’ll want to pass the `--disable-tls` flag so that it will accept an `http` connection + +## Reducing Cost With S3 Storage Classes + +AWS S3 offers [storage classes](https://aws.amazon.com/s3/storage-classes/) for a variety of different use cases and +Corso can leverage a number of them, but not all, to reduce the cost of storing data in the cloud. + +By default, Corso works hard to reduce its data footprint. It will compress and deduplicate data at source to reduce the +amount of storage used as well as the amount of network traffic when writing to object storage. Corso also combines +different emails, attachments, etc. into larger objects to make it more cost-effective by reducing the number of API +calls and increasing network throughput as well as making Corso data eligible and cost-effective for some of the other +storage classes described below. + +Stepping away from the default S3 offering (S3 Standard), S3 offers a number of different Glacier (cheap and deep) +storage classes that can help to further reduce the cost for backup and archival workloads. Within the storage classes, +Corso today supports Glacier Instant Retrieval but, because of user responsiveness and metadata requirements, not the +other Glacier variants. + +Glacier Instant Retrieval should provide the best price performance for a backup workload as backup data blobs are +typically written once, with occasional re-compacting, and read infrequently in the case of restore. One should note +that recommendations such as these are always workload dependent and should be verified for your use case. For example, +we would not recommend Glacier Instant Retrieval if you are constantly testing large restores or have heavy +churn in your backups and +limited retention. However, for most typical backup workloads (write mostly, read rarely), Glacier Instant Retrieval +should work just fine and deliver the best price-performance ratio. + +You can configure your storage to use Glacier Instant Retrieval by adding a `.storageconfig` file to the root of your +bucket. If you have configured Corso to store the repository in a sub-folder within your bucket by adding a +`prefix = '[folder name]'` configuration, the `.storageconfig` should go within that folder in the bucket. + +Here’s an example: + +```json +{ + "blobOptions": [ + { "prefix": "p", "storageClass": "GLACIER_IR" }, + { "storageClass": "STANDARD" } + ] +} +``` + +The `"prefix": "p"` parameter is unrelated to the subfolder `prefix` setting mentioned above. It tells Corso to +use the selected storage class for data blobs (named with a `p` prefix). By default, all other objects including +metadata and indices will use the standard storage tier. + +We would love to hear from you if you’ve deployed Corso with a different storage class, an object storage provider not +listed above, or have questions about how to best cost-optimize your setup. Come [find us on Discord](https://discord.gg/63DTTSnuhT)! diff --git a/website/blog/images/boxes_web.jpeg b/website/blog/images/boxes_web.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..840e96526e084922750b1e72f6a5975df6425781 GIT binary patch literal 354646 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

KtlxK0ka$1u4q*hr8$oZw2n(BENq)xU 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%0Dw7Yf39(- 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%0Dod$WIvdLC_V9P~h1P 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

)oA@jH4VfAfe@;4(o4RiI3rXL3ePH9vU2 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

kdPuI9PILi=my6O}&yWzLsV@2SsIhMpP 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 literal 0 HcmV?d00001 diff --git a/website/styles/Vocab/Base/accept.txt b/website/styles/Vocab/Base/accept.txt index 69565b8a5..8831b2ca6 100644 --- a/website/styles/Vocab/Base/accept.txt +++ b/website/styles/Vocab/Base/accept.txt @@ -37,4 +37,6 @@ SLAs runbooks stdout stderr -backoff \ No newline at end of file +backoff +Greenlake +subfolder \ No newline at end of file From 0436e0d128cb5ed85b8f99560ab876b8c36bef3a Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 2 Feb 2023 19:01:42 -0700 Subject: [PATCH 40/46] extra panic protection in operations (#2383) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :broom: Tech Debt/Cleanup ## Test Plan - [x] :green_heart: E2E --- src/internal/operations/backup.go | 33 +++++++++++++++++++++++++----- src/internal/operations/restore.go | 19 ++++++++++++++--- src/pkg/fault/fault.go | 7 ++++--- src/pkg/fault/fault_test.go | 4 ++++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index 8e40d820f..e0df72d55 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -2,6 +2,7 @@ package operations import ( "context" + "runtime/debug" "time" "github.com/alcionai/clues" @@ -106,7 +107,13 @@ type detailsWriter interface { // --------------------------------------------------------------------------- // Run begins a synchronous backup operation. -func (op *BackupOperation) Run(ctx context.Context) error { +func (op *BackupOperation) Run(ctx context.Context) (err error) { + defer func() { + if r := recover(); r != nil { + err = clues.Wrap(r.(error), "panic recovery").WithClues(ctx).With("stacktrace", debug.Stack()) + } + }() + ctx, end := D.Span(ctx, "operations:backup:run") defer func() { end() @@ -189,6 +196,8 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { op.Errors.Fail(errors.Wrap(err, "collecting manifest heuristics")) opStats.readErr = op.Errors.Err() + logger.Ctx(ctx).With("err", err).Errorw("producing manifests and metadata", clues.InErr(err).Slice()...) + return opStats.readErr } @@ -197,6 +206,8 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { op.Errors.Fail(errors.Wrap(err, "connecting to m365")) opStats.readErr = op.Errors.Err() + logger.Ctx(ctx).With("err", err).Errorw("connectng to m365", clues.InErr(err).Slice()...) + return opStats.readErr } @@ -205,6 +216,8 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { op.Errors.Fail(errors.Wrap(err, "retrieving data to backup")) opStats.readErr = op.Errors.Err() + logger.Ctx(ctx).With("err", err).Errorw("producing backup data collections", clues.InErr(err).Slice()...) + return opStats.readErr } @@ -223,6 +236,8 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { op.Errors.Fail(errors.Wrap(err, "backing up service data")) opStats.writeErr = op.Errors.Err() + logger.Ctx(ctx).With("err", err).Errorw("persisting collection backups", clues.InErr(err).Slice()...) + return opStats.writeErr } @@ -237,6 +252,8 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { op.Errors.Fail(errors.Wrap(err, "merging backup details")) opStats.writeErr = op.Errors.Err() + logger.Ctx(ctx).With("err", err).Errorw("merging details", clues.InErr(err).Slice()...) + return opStats.writeErr } @@ -589,15 +606,21 @@ func (op *BackupOperation) persistResults( opStats.writeErr) } + op.Results.BytesRead = opStats.k.TotalHashedBytes + op.Results.BytesUploaded = opStats.k.TotalUploadedBytes + op.Results.ItemsWritten = opStats.k.TotalFileCount + op.Results.ResourceOwners = opStats.resourceCount + + if opStats.gc == nil { + op.Status = Failed + return errors.New("data population never completed") + } + if opStats.readErr == nil && opStats.writeErr == nil && opStats.gc.Successful == 0 { op.Status = NoData } - op.Results.BytesRead = opStats.k.TotalHashedBytes - op.Results.BytesUploaded = opStats.k.TotalUploadedBytes op.Results.ItemsRead = opStats.gc.Successful - op.Results.ItemsWritten = opStats.k.TotalFileCount - op.Results.ResourceOwners = opStats.resourceCount return nil } diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index 30bc303a9..aa9229336 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -3,6 +3,7 @@ package operations import ( "context" "fmt" + "runtime/debug" "time" "github.com/alcionai/clues" @@ -106,6 +107,12 @@ type restorer interface { // Run begins a synchronous restore operation. func (op *RestoreOperation) Run(ctx context.Context) (restoreDetails *details.Details, err error) { + defer func() { + if r := recover(); r != nil { + err = clues.Wrap(r.(error), "panic recovery").WithClues(ctx).With("stacktrace", debug.Stack()) + } + }() + ctx, end := D.Span(ctx, "operations:restore:run") defer func() { end() @@ -250,14 +257,20 @@ func (op *RestoreOperation) persistResults( opStats.writeErr) } + op.Results.BytesRead = opStats.bytesRead.NumBytes + op.Results.ItemsRead = len(opStats.cs) // TODO: file count, not collection count + op.Results.ResourceOwners = opStats.resourceCount + + if opStats.gc == nil { + op.Status = Failed + return errors.New("data restoration never completed") + } + if opStats.readErr == nil && opStats.writeErr == nil && opStats.gc.Successful == 0 { op.Status = NoData } - op.Results.BytesRead = opStats.bytesRead.NumBytes - op.Results.ItemsRead = len(opStats.cs) // TODO: file count, not collection count op.Results.ItemsWritten = opStats.gc.Successful - op.Results.ResourceOwners = opStats.resourceCount dur := op.Results.CompletedAt.Sub(op.Results.StartedAt) diff --git a/src/pkg/fault/fault.go b/src/pkg/fault/fault.go index 69017029c..b8f57108b 100644 --- a/src/pkg/fault/fault.go +++ b/src/pkg/fault/fault.go @@ -87,11 +87,12 @@ func (e *Errors) Fail(err error) *Errors { // setErr handles setting errors.err. Sync locking gets // handled upstream of this call. func (e *Errors) setErr(err error) *Errors { - if e.err != nil { - return e.addErr(err) + if e.err == nil { + e.err = err + return e } - e.err = err + e.errs = append(e.errs, err) return e } diff --git a/src/pkg/fault/fault_test.go b/src/pkg/fault/fault_test.go index 3f5ad127c..8fb2981d7 100644 --- a/src/pkg/fault/fault_test.go +++ b/src/pkg/fault/fault_test.go @@ -73,6 +73,8 @@ func (suite *FaultErrorsUnitSuite) TestErr() { suite.T().Run(test.name, func(t *testing.T) { n := fault.New(test.failFast) require.NotNil(t, n) + require.NoError(t, n.Err()) + require.Empty(t, n.Errs()) e := n.Fail(test.fail) require.NotNil(t, e) @@ -90,6 +92,8 @@ func (suite *FaultErrorsUnitSuite) TestFail() { n := fault.New(false) require.NotNil(t, n) + require.NoError(t, n.Err()) + require.Empty(t, n.Errs()) n.Fail(assert.AnError) assert.Error(t, n.Err()) From b8bc85deba3b0c9e386b6e98545b087809a04025 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 3 Feb 2023 08:55:51 +0530 Subject: [PATCH 41/46] Metadata backup for OneDrive (#2148) ## Description This PR adds option to backup and restore additional metadata(currently permissions) for OneDrive. **This PR also adds the ability to restore empty folders for OneDrive along with their permissions.** Breaking change: Any old backups will not work as we expect both `.data` and `.meta`/`.dirmeta` files to be available for OneDrive backups. ## Does this PR need a docs update or release note? *Added changelog, docs pending.* - [x] :white_check_mark: Yes, it's included - [x] :clock1: Yes, but in a later PR - [ ] :no_entry: No ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * https://github.com/alcionai/corso/issues/1774 ## Test Plan - [ ] :muscle: Manual - [x] :zap: Unit test - [x] :green_heart: E2E --- CHANGELOG.md | 8 + src/cli/backup/onedrive.go | 1 + src/cli/options/options.go | 45 +- src/cli/restore/onedrive.go | 6 + src/cmd/factory/impl/common.go | 5 +- src/cmd/factory/impl/exchange.go | 4 + src/internal/connector/graph_connector.go | 3 +- .../connector/graph_connector_helper_test.go | 62 ++- .../connector/graph_connector_test.go | 510 +++++++++++++++++- src/internal/connector/onedrive/collection.go | 275 +++++++--- .../connector/onedrive/collection_test.go | 163 +++++- .../connector/onedrive/collections.go | 15 +- .../connector/onedrive/collections_test.go | 177 +++--- src/internal/connector/onedrive/item.go | 80 ++- src/internal/connector/onedrive/item_test.go | 2 +- src/internal/connector/onedrive/restore.go | 415 ++++++++++++-- .../sharepoint/data_collections_test.go | 2 +- src/internal/connector/sharepoint/restore.go | 8 +- src/internal/connector/support/status.go | 1 + .../operations/backup_integration_test.go | 2 +- src/internal/operations/restore.go | 16 +- src/pkg/control/options.go | 14 +- 22 files changed, 1540 insertions(+), 274 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50a5f3270..33485d926 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Document Corso's fault-tolerance and restartability features - Add retries on timeouts and status code 500 for Exchange - Increase page size preference for delta requests for Exchange to reduce number of roundtrips +- OneDrive file/folder permissions can now be backed up and restored +- Add `--restore-permissions` flag to toggle restoration of OneDrive permissions + +### Known Issues + +- When the same user has permissions to a file and the containing + folder, we only restore folder level permissions for the user and no + separate file only permission is restored. ## [v0.2.0] (alpha) - 2023-1-29 diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index 60a055dce..f74f98916 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -79,6 +79,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command { switch cmd.Use { case createCommand: c, fs = utils.AddCommand(cmd, oneDriveCreateCmd()) + options.AddFeatureToggle(cmd, options.DisablePermissionsBackup()) c.Use = c.Use + " " + oneDriveServiceCommandCreateUseSuffix c.Example = oneDriveServiceCommandCreateExamples diff --git a/src/cli/options/options.go b/src/cli/options/options.go index 4988c29ca..32defc5bb 100644 --- a/src/cli/options/options.go +++ b/src/cli/options/options.go @@ -11,17 +11,11 @@ import ( func Control() control.Options { opt := control.Defaults() - if fastFail { - opt.FailFast = true - } - - if noStats { - opt.DisableMetrics = true - } - - if disableIncrementals { - opt.ToggleFeatures.DisableIncrementals = true - } + opt.FailFast = fastFail + opt.DisableMetrics = noStats + opt.RestorePermissions = restorePermissions + opt.ToggleFeatures.DisableIncrementals = disableIncrementals + opt.ToggleFeatures.DisablePermissionsBackup = disablePermissionsBackup return opt } @@ -31,8 +25,9 @@ func Control() control.Options { // --------------------------------------------------------------------------- var ( - fastFail bool - noStats bool + fastFail bool + noStats bool + restorePermissions bool ) // AddOperationFlags adds command-local operation flags @@ -49,11 +44,20 @@ func AddGlobalOperationFlags(cmd *cobra.Command) { fs.BoolVar(&noStats, "no-stats", false, "disable anonymous usage statistics gathering") } +// AddRestorePermissionsFlag adds OneDrive flag for restoring permissions +func AddRestorePermissionsFlag(cmd *cobra.Command) { + fs := cmd.Flags() + fs.BoolVar(&restorePermissions, "restore-permissions", false, "Restore permissions for files and folders") +} + // --------------------------------------------------------------------------- // Feature Flags // --------------------------------------------------------------------------- -var disableIncrementals bool +var ( + disableIncrementals bool + disablePermissionsBackup bool +) type exposeFeatureFlag func(*pflag.FlagSet) @@ -78,3 +82,16 @@ func DisableIncrementals() func(*pflag.FlagSet) { cobra.CheckErr(fs.MarkHidden("disable-incrementals")) } } + +// Adds the hidden '--disable-permissions-backup' cli flag which, when +// set, disables backing up permissions. +func DisablePermissionsBackup() func(*pflag.FlagSet) { + return func(fs *pflag.FlagSet) { + fs.BoolVar( + &disablePermissionsBackup, + "disable-permissions-backup", + false, + "Disable backing up item permissions for OneDrive") + cobra.CheckErr(fs.MarkHidden("disable-permissions-backup")) + } +} diff --git a/src/cli/restore/onedrive.go b/src/cli/restore/onedrive.go index 526db414b..bd8dc7816 100644 --- a/src/cli/restore/onedrive.go +++ b/src/cli/restore/onedrive.go @@ -63,6 +63,9 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command { utils.FileFN, nil, "Restore items by file name or ID") + // permissions restore flag + options.AddRestorePermissionsFlag(c) + // onedrive info flags fs.StringVar( @@ -97,6 +100,9 @@ const ( oneDriveServiceCommandRestoreExamples = `# Restore file with ID 98765abcdef corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef +# Restore file with ID 98765abcdef along with its associated permissions +corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd --file 98765abcdef --restore-permissions + # Restore Alice's file named "FY2021 Planning.xlsx in "Documents/Finance Reports" from a specific backup corso restore onedrive --backup 1234abcd-12ab-cd34-56de-1234abcd \ --user alice@example.com --file "FY2021 Planning.xlsx" --folder "Documents/Finance Reports" diff --git a/src/cmd/factory/impl/common.go b/src/cmd/factory/impl/common.go index 0ea6835dd..3ed3831fc 100644 --- a/src/cmd/factory/impl/common.go +++ b/src/cmd/factory/impl/common.go @@ -50,6 +50,7 @@ func generateAndRestoreItems( tenantID, userID, destFldr string, howMany int, dbf dataBuilderFunc, + opts control.Options, ) (*details.Details, error) { items := make([]item, 0, howMany) @@ -74,7 +75,7 @@ func generateAndRestoreItems( items: items, }} - // TODO: fit the desination to the containers + // TODO: fit the destination to the containers dest := control.DefaultRestoreDestination(common.SimpleTimeTesting) dest.ContainerName = destFldr @@ -90,7 +91,7 @@ func generateAndRestoreItems( Infof(ctx, "Generating %d %s items in %s\n", howMany, cat, Destination) - return gc.RestoreDataCollections(ctx, acct, sel, dest, dataColls) + return gc.RestoreDataCollections(ctx, acct, sel, dest, opts, dataColls) } // ------------------------------------------------------------------------------------------ diff --git a/src/cmd/factory/impl/exchange.go b/src/cmd/factory/impl/exchange.go index 26f7eef09..39e3c13a1 100644 --- a/src/cmd/factory/impl/exchange.go +++ b/src/cmd/factory/impl/exchange.go @@ -6,6 +6,7 @@ import ( . "github.com/alcionai/corso/src/cli/print" "github.com/alcionai/corso/src/cli/utils" "github.com/alcionai/corso/src/internal/connector/mockconnector" + "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/selectors" ) @@ -67,6 +68,7 @@ func handleExchangeEmailFactory(cmd *cobra.Command, args []string) error { subject, body, body, now, now, now, now) }, + control.Options{}, ) if err != nil { return Only(ctx, err) @@ -107,6 +109,7 @@ func handleExchangeCalendarEventFactory(cmd *cobra.Command, args []string) error User, subject, body, body, now, now, false) }, + control.Options{}, ) if err != nil { return Only(ctx, err) @@ -152,6 +155,7 @@ func handleExchangeContactFactory(cmd *cobra.Command, args []string) error { "123-456-7890", ) }, + control.Options{}, ) if err != nil { return Only(ctx, err) diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index 3dbc0e60c..370948639 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -269,6 +269,7 @@ func (gc *GraphConnector) RestoreDataCollections( acct account.Account, selector selectors.Selector, dest control.RestoreDestination, + opts control.Options, dcs []data.Collection, ) (*details.Details, error) { ctx, end := D.Span(ctx, "connector:restore") @@ -289,7 +290,7 @@ func (gc *GraphConnector) RestoreDataCollections( case selectors.ServiceExchange: status, err = exchange.RestoreExchangeDataCollections(ctx, creds, gc.Service, dest, dcs, deets) case selectors.ServiceOneDrive: - status, err = onedrive.RestoreCollections(ctx, gc.Service, dest, dcs, deets) + status, err = onedrive.RestoreCollections(ctx, gc.Service, dest, opts, dcs, deets) case selectors.ServiceSharePoint: status, err = sharepoint.RestoreCollections(ctx, gc.Service, dest, dcs, deets) default: diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index 698ee8527..8a0e22a26 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -2,9 +2,11 @@ package connector import ( "context" + "encoding/json" "io" "net/http" "reflect" + "strings" "testing" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -14,6 +16,7 @@ import ( "golang.org/x/exp/maps" "github.com/alcionai/corso/src/internal/connector/mockconnector" + "github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" @@ -645,21 +648,52 @@ func compareOneDriveItem( t *testing.T, expected map[string][]byte, item data.Stream, + restorePermissions bool, ) { + name := item.UUID() + expectedData := expected[item.UUID()] if !assert.NotNil(t, expectedData, "unexpected file with name %s", item.UUID) { return } - // OneDrive items are just byte buffers of the data. Nothing special to - // interpret. May need to do chunked comparisons in the future if we test - // large item equality. buf, err := io.ReadAll(item.ToReader()) if !assert.NoError(t, err) { return } - assert.Equal(t, expectedData, buf) + if !strings.HasSuffix(name, onedrive.MetaFileSuffix) && !strings.HasSuffix(name, onedrive.DirMetaFileSuffix) { + // OneDrive data items are just byte buffers of the data. Nothing special to + // interpret. May need to do chunked comparisons in the future if we test + // large item equality. + assert.Equal(t, expectedData, buf) + return + } + + var ( + itemMeta onedrive.Metadata + expectedMeta onedrive.Metadata + ) + + err = json.Unmarshal(buf, &itemMeta) + assert.Nil(t, err) + + err = json.Unmarshal(expectedData, &expectedMeta) + assert.Nil(t, err) + + if !restorePermissions { + assert.Equal(t, 0, len(itemMeta.Permissions)) + return + } + + assert.Equal(t, len(expectedMeta.Permissions), len(itemMeta.Permissions), "number of permissions after restore") + + // FIXME(meain): The permissions before and after might not be in the same order. + for i, p := range expectedMeta.Permissions { + assert.Equal(t, p.Email, itemMeta.Permissions[i].Email) + assert.Equal(t, p.Roles, itemMeta.Permissions[i].Roles) + assert.Equal(t, p.Expiration, itemMeta.Permissions[i].Expiration) + } } func compareItem( @@ -668,6 +702,7 @@ func compareItem( service path.ServiceType, category path.CategoryType, item data.Stream, + restorePermissions bool, ) { if mt, ok := item.(data.StreamModTime); ok { assert.NotZero(t, mt.ModTime()) @@ -687,7 +722,7 @@ func compareItem( } case path.OneDriveService: - compareOneDriveItem(t, expected, item) + compareOneDriveItem(t, expected, item, restorePermissions) default: assert.FailNowf(t, "unexpected service: %s", service.String()) @@ -720,6 +755,7 @@ func checkCollections( expectedItems int, expected map[string]map[string][]byte, got []data.Collection, + restorePermissions bool, ) int { collectionsWithItems := []data.Collection{} @@ -754,7 +790,7 @@ func checkCollections( continue } - compareItem(t, expectedColData, service, category, item) + compareItem(t, expectedColData, service, category, item, restorePermissions) } if gotItems != startingItems { @@ -906,10 +942,11 @@ func collectionsForInfo( tenant, user string, dest control.RestoreDestination, allInfo []colInfo, -) (int, []data.Collection, map[string]map[string][]byte) { +) (int, int, []data.Collection, map[string]map[string][]byte) { collections := make([]data.Collection, 0, len(allInfo)) expectedData := make(map[string]map[string][]byte, len(allInfo)) totalItems := 0 + kopiaEntries := 0 for _, info := range allInfo { pth := mustToDataLayerPath( @@ -935,13 +972,20 @@ func collectionsForInfo( c.Data[i] = info.items[i].data baseExpected[info.items[i].lookupKey] = info.items[i].data + + // We do not count metadata files against item count + if service != path.OneDriveService || + (service == path.OneDriveService && + strings.HasSuffix(info.items[i].name, onedrive.DataFileSuffix)) { + totalItems++ + } } collections = append(collections, c) - totalItems += len(info.items) + kopiaEntries += len(info.items) } - return totalItems, collections, expectedData + return totalItems, kopiaEntries, collections, expectedData } //nolint:deadcode diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index be1439c35..2c2280e37 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -2,6 +2,8 @@ package connector import ( "context" + "encoding/base64" + "encoding/json" "strings" "testing" "time" @@ -15,6 +17,7 @@ import ( "github.com/alcionai/corso/src/internal/connector/discovery/api" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/mockconnector" + "github.com/alcionai/corso/src/internal/connector/onedrive" "github.com/alcionai/corso/src/internal/connector/support" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" @@ -135,9 +138,10 @@ func (suite *GraphConnectorUnitSuite) TestUnionSiteIDsAndWebURLs() { type GraphConnectorIntegrationSuite struct { suite.Suite - connector *GraphConnector - user string - acct account.Account + connector *GraphConnector + user string + secondaryUser string + acct account.Account } func TestGraphConnectorIntegrationSuite(t *testing.T) { @@ -158,6 +162,7 @@ func (suite *GraphConnectorIntegrationSuite) SetupSuite() { suite.connector = loadConnector(ctx, suite.T(), graph.HTTPClient(graph.NoTimeout()), Users) suite.user = tester.M365UserID(suite.T()) + suite.secondaryUser = tester.SecondaryM365UserID(suite.T()) suite.acct = tester.NewM365Account(suite.T()) tester.LogTimeOfTest(suite.T()) @@ -226,7 +231,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreFailsBadService() { } ) - deets, err := suite.connector.RestoreDataCollections(ctx, acct, sel, dest, nil) + deets, err := suite.connector.RestoreDataCollections(ctx, acct, sel, dest, control.Options{}, nil) assert.Error(t, err) assert.NotNil(t, deets) @@ -297,7 +302,9 @@ func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() { suite.acct, test.sel, dest, - test.col) + control.Options{RestorePermissions: true}, + test.col, + ) require.NoError(t, err) assert.NotNil(t, deets) @@ -344,11 +351,13 @@ func runRestoreBackupTest( test restoreBackupInfo, tenant string, resourceOwners []string, + opts control.Options, ) { var ( - collections []data.Collection - expectedData = map[string]map[string][]byte{} - totalItems = 0 + collections []data.Collection + expectedData = map[string]map[string][]byte{} + totalItems = 0 + totalKopiaItems = 0 // Get a dest per test so they're independent. dest = tester.DefaultTestRestoreDestination() ) @@ -357,7 +366,7 @@ func runRestoreBackupTest( defer flush() for _, owner := range resourceOwners { - numItems, ownerCollections, userExpectedData := collectionsForInfo( + numItems, kopiaItems, ownerCollections, userExpectedData := collectionsForInfo( t, test.service, tenant, @@ -368,6 +377,7 @@ func runRestoreBackupTest( collections = append(collections, ownerCollections...) totalItems += numItems + totalKopiaItems += kopiaItems maps.Copy(expectedData, userExpectedData) } @@ -386,7 +396,9 @@ func runRestoreBackupTest( acct, restoreSel, dest, - collections) + opts, + collections, + ) require.NoError(t, err) assert.NotNil(t, deets) @@ -425,7 +437,7 @@ func runRestoreBackupTest( t.Logf("Selective backup of %s\n", backupSel) start = time.Now() - dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) + dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) require.NoError(t, err) // No excludes yet because this isn't an incremental backup. assert.Empty(t, excludes) @@ -434,7 +446,7 @@ func runRestoreBackupTest( // Pull the data prior to waiting for the status as otherwise it will // deadlock. - skipped := checkCollections(t, totalItems, expectedData, dcs) + skipped := checkCollections(t, totalKopiaItems, expectedData, dcs, opts.RestorePermissions) status = backupGC.AwaitStatus() @@ -446,6 +458,20 @@ func runRestoreBackupTest( "backup status.Successful; wanted %d items + %d skipped", totalItems, skipped) } +func getTestMetaJSON(t *testing.T, user string, roles []string) []byte { + id := base64.StdEncoding.EncodeToString([]byte(user + strings.Join(roles, "+"))) + testMeta := onedrive.Metadata{Permissions: []onedrive.UserPermission{ + {ID: id, Roles: roles, Email: user}, + }} + + testMetaJSON, err := json.Marshal(testMeta) + if err != nil { + t.Fatal("unable to marshall test permissions", err) + } + + return testMetaJSON +} + func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { bodyText := "This email has some text. However, all the text is on the same line." subjectText := "Test message for restore" @@ -564,7 +590,7 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { }, }, { - name: "MultipleContactsMutlipleFolders", + name: "MultipleContactsMultipleFolders", service: path.ExchangeService, resource: Users, collections: []colInfo{ @@ -691,9 +717,24 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { category: path.FilesCategory, items: []itemInfo{ { - name: "test-file.txt", + name: "test-file.txt" + onedrive.DataFileSuffix, data: []byte(strings.Repeat("a", 33)), - lookupKey: "test-file.txt", + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "folder-a" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "folder-a" + onedrive.DirMetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "b" + onedrive.DirMetaFileSuffix, }, }, }, @@ -707,9 +748,19 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { category: path.FilesCategory, items: []itemInfo{ { - name: "test-file.txt", + name: "test-file.txt" + onedrive.DataFileSuffix, data: []byte(strings.Repeat("b", 65)), - lookupKey: "test-file.txt", + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "b" + onedrive.DirMetaFileSuffix, }, }, }, @@ -724,9 +775,19 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { category: path.FilesCategory, items: []itemInfo{ { - name: "test-file.txt", + name: "test-file.txt" + onedrive.DataFileSuffix, data: []byte(strings.Repeat("c", 129)), - lookupKey: "test-file.txt", + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "folder-a" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "folder-a" + onedrive.DirMetaFileSuffix, }, }, }, @@ -742,9 +803,14 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { category: path.FilesCategory, items: []itemInfo{ { - name: "test-file.txt", + name: "test-file.txt" + onedrive.DataFileSuffix, data: []byte(strings.Repeat("d", 257)), - lookupKey: "test-file.txt", + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, }, }, }, @@ -758,9 +824,67 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { category: path.FilesCategory, items: []itemInfo{ { - name: "test-file.txt", + name: "test-file.txt" + onedrive.DataFileSuffix, data: []byte(strings.Repeat("e", 257)), - lookupKey: "test-file.txt", + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + { + name: "OneDriveFoldersAndFilesWithMetadata", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"write"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 66)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, }, }, }, @@ -770,7 +894,14 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { for _, test := range table { suite.T().Run(test.name, func(t *testing.T) { - runRestoreBackupTest(t, suite.acct, test, suite.connector.tenant, []string{suite.user}) + runRestoreBackupTest( + t, + suite.acct, + test, + suite.connector.tenant, + []string{suite.user}, + control.Options{RestorePermissions: true}, + ) }) } } @@ -857,7 +988,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames }, }) - totalItems, collections, expectedData := collectionsForInfo( + totalItems, _, collections, expectedData := collectionsForInfo( t, test.service, suite.connector.tenant, @@ -879,7 +1010,14 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames ) restoreGC := loadConnector(ctx, t, graph.HTTPClient(graph.NoTimeout()), test.resource) - deets, err := restoreGC.RestoreDataCollections(ctx, suite.acct, restoreSel, dest, collections) + deets, err := restoreGC.RestoreDataCollections( + ctx, + suite.acct, + restoreSel, + dest, + control.Options{RestorePermissions: true}, + collections, + ) require.NoError(t, err) require.NotNil(t, deets) @@ -900,7 +1038,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames backupSel := backupSelectorForExpected(t, test.service, expectedDests) t.Log("Selective backup of", backupSel) - dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{}) + dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) require.NoError(t, err) // No excludes yet because this isn't an incremental backup. assert.Empty(t, excludes) @@ -909,7 +1047,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames // Pull the data prior to waiting for the status as otherwise it will // deadlock. - skipped := checkCollections(t, allItems, allExpectedData, dcs) + skipped := checkCollections(t, allItems, allExpectedData, dcs, true) status := backupGC.AwaitStatus() assert.Equal(t, allItems+skipped, status.ObjectCount, "status.ObjectCount") @@ -918,6 +1056,313 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames } } +func (suite *GraphConnectorIntegrationSuite) TestPermissionsRestoreAndBackup() { + ctx, flush := tester.NewContext() + defer flush() + + // Get the default drive ID for the test user. + driveID := mustGetDefaultDriveID( + suite.T(), + ctx, + suite.connector.Service, + suite.user, + ) + + table := []restoreBackupInfo{ + { + name: "FilePermissionsResote", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"write"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + + { + name: "FileInsideFolderPermissionsRestore", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 66)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + + { + name: "FileAndFolderPermissionsResote", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"write"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 66)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + + { + name: "FileAndFolderSeparatePermissionsResote", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "b" + onedrive.DirMetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 66)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"write"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + + { + name: "FolderAndNoChildPermissionsResote", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "b" + onedrive.DirMetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"read"}), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 66)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + runRestoreBackupTest(t, + suite.acct, + test, + suite.connector.tenant, + []string{suite.user}, + control.Options{RestorePermissions: true}, + ) + }) + } +} + +func (suite *GraphConnectorIntegrationSuite) TestPermissionsBackupAndNoRestore() { + ctx, flush := tester.NewContext() + defer flush() + + // Get the default drive ID for the test user. + driveID := mustGetDefaultDriveID( + suite.T(), + ctx, + suite.connector.Service, + suite.user, + ) + + table := []restoreBackupInfo{ + { + name: "FilePermissionsResote", + service: path.OneDriveService, + resource: Users, + collections: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: getTestMetaJSON(suite.T(), suite.secondaryUser, []string{"write"}), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + runRestoreBackupTest( + t, + suite.acct, + test, + suite.connector.tenant, + []string{suite.user}, + control.Options{RestorePermissions: false}, + ) + }) + } +} + // TODO: this should only be run during smoke tests, not part of the standard CI. // That's why it's set aside instead of being included in the other test set. func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup_largeMailAttachment() { @@ -942,5 +1387,12 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup_largeMailAttac }, } - runRestoreBackupTest(suite.T(), suite.acct, test, suite.connector.tenant, []string{suite.user}) + runRestoreBackupTest( + suite.T(), + suite.acct, + test, + suite.connector.tenant, + []string{suite.user}, + control.Options{RestorePermissions: true}, + ) } diff --git a/src/internal/connector/onedrive/collection.go b/src/internal/connector/onedrive/collection.go index c4e1825bd..3b8ff5cbb 100644 --- a/src/internal/connector/onedrive/collection.go +++ b/src/internal/connector/onedrive/collection.go @@ -5,6 +5,7 @@ import ( "context" "io" "net/http" + "strings" "sync" "sync/atomic" "time" @@ -34,6 +35,10 @@ const ( // Max number of retries to get doc from M365 // Seems to timeout at times because of multiple requests maxRetries = 4 // 1 + 3 retries + + MetaFileSuffix = ".meta" + DirMetaFileSuffix = ".dirmeta" + DataFileSuffix = ".data" ) var ( @@ -56,12 +61,13 @@ type Collection struct { // M365 IDs of file items within this collection driveItems map[string]models.DriveItemable // M365 ID of the drive this collection was created from - driveID string - source driveSource - service graph.Servicer - statusUpdater support.StatusUpdater - itemReader itemReaderFunc - ctrl control.Options + driveID string + source driveSource + service graph.Servicer + statusUpdater support.StatusUpdater + itemReader itemReaderFunc + itemMetaReader itemMetaReaderFunc + ctrl control.Options // should only be true if the old delta token expired doNotMergeItems bool @@ -73,6 +79,15 @@ type itemReaderFunc func( item models.DriveItemable, ) (itemInfo details.ItemInfo, itemData io.ReadCloser, err error) +// itemMetaReaderFunc returns a reader for the metadata of the +// specified item +type itemMetaReaderFunc func( + ctx context.Context, + service graph.Servicer, + driveID string, + item models.DriveItemable, +) (io.ReadCloser, int, error) + // NewCollection creates a Collection func NewCollection( itemClient *http.Client, @@ -101,6 +116,7 @@ func NewCollection( c.itemReader = sharePointItemReader default: c.itemReader = oneDriveItemReader + c.itemMetaReader = oneDriveItemMetaReader } return c @@ -138,6 +154,21 @@ func (oc Collection) DoNotMergeItems() bool { return oc.doNotMergeItems } +// FilePermission is used to store permissions of a specific user to a +// OneDrive item. +type UserPermission struct { + ID string `json:"id,omitempty"` + Roles []string `json:"role,omitempty"` + Email string `json:"email,omitempty"` + Expiration *time.Time `json:"expiration,omitempty"` +} + +// ItemMeta contains metadata about the Item. It gets stored in a +// separate file in kopia +type Metadata struct { + Permissions []UserPermission `json:"permissions,omitempty"` +} + // Item represents a single item retrieved from OneDrive type Item struct { id string @@ -173,18 +204,21 @@ func (od *Item) ModTime() time.Time { // and uses the collection `itemReader` to read the item func (oc *Collection) populateItems(ctx context.Context) { var ( - errs error - byteCount int64 - itemsRead int64 - wg sync.WaitGroup - m sync.Mutex + errs error + byteCount int64 + itemsRead int64 + dirsRead int64 + itemsFound int64 + dirsFound int64 + wg sync.WaitGroup + m sync.Mutex ) // Retrieve the OneDrive folder path to set later in // `details.OneDriveInfo` parentPathString, err := path.GetDriveFolderPath(oc.folderPath) if err != nil { - oc.reportAsCompleted(ctx, 0, 0, err) + oc.reportAsCompleted(ctx, 0, 0, 0, err) return } @@ -205,16 +239,11 @@ func (oc *Collection) populateItems(ctx context.Context) { m.Unlock() } - for id, item := range oc.driveItems { + for _, item := range oc.driveItems { if oc.ctrl.FailFast && errs != nil { break } - if item == nil { - errUpdater(id, errors.New("nil item")) - continue - } - semaphoreCh <- struct{}{} wg.Add(1) @@ -223,13 +252,61 @@ func (oc *Collection) populateItems(ctx context.Context) { defer wg.Done() defer func() { <-semaphoreCh }() + // Read the item var ( - itemID = *item.GetId() - itemName = *item.GetName() - itemSize = *item.GetSize() - itemInfo details.ItemInfo + itemID = *item.GetId() + itemName = *item.GetName() + itemSize = *item.GetSize() + itemInfo details.ItemInfo + itemMeta io.ReadCloser + itemMetaSize int + metaSuffix string + err error ) + isFile := item.GetFile() != nil + + if isFile { + atomic.AddInt64(&itemsFound, 1) + + metaSuffix = MetaFileSuffix + } else { + atomic.AddInt64(&dirsFound, 1) + + metaSuffix = DirMetaFileSuffix + } + + if oc.source == OneDriveSource { + // Fetch metadata for the file + for i := 1; i <= maxRetries; i++ { + if oc.ctrl.ToggleFeatures.DisablePermissionsBackup { + // We are still writing the metadata file but with + // empty permissions as we are not sure how the + // restore will be called. + itemMeta = io.NopCloser(strings.NewReader("{}")) + itemMetaSize = 2 + + break + } + + itemMeta, itemMetaSize, err = oc.itemMetaReader(ctx, oc.service, oc.driveID, item) + + // retry on Timeout type errors, break otherwise. + if err == nil || !graph.IsErrTimeout(err) { + break + } + + if i < maxRetries { + time.Sleep(1 * time.Second) + } + } + + if err != nil { + errUpdater(*item.GetId(), err) + return + } + } + switch oc.source { case SharePointSource: itemInfo.SharePoint = sharePointItemInfo(item, itemSize) @@ -239,101 +316,127 @@ func (oc *Collection) populateItems(ctx context.Context) { itemInfo.OneDrive.ParentPath = parentPathString } - // Construct a new lazy readCloser to feed to the collection consumer. - // This ensures that downloads won't be attempted unless that consumer - // attempts to read bytes. Assumption is that kopia will check things - // like file modtimes before attempting to read. - itemReader := lazy.NewLazyReadCloser(func() (io.ReadCloser, error) { - // Read the item - var ( - itemData io.ReadCloser - err error - ) + if isFile { + dataSuffix := "" + if oc.source == OneDriveSource { + dataSuffix = DataFileSuffix + } - for i := 1; i <= maxRetries; i++ { - _, itemData, err = oc.itemReader(oc.itemClient, item) - if err == nil { - break - } + // Construct a new lazy readCloser to feed to the collection consumer. + // This ensures that downloads won't be attempted unless that consumer + // attempts to read bytes. Assumption is that kopia will check things + // like file modtimes before attempting to read. + itemReader := lazy.NewLazyReadCloser(func() (io.ReadCloser, error) { + // Read the item + var ( + itemData io.ReadCloser + err error + ) - if graph.IsErrUnauthorized(err) { - // assume unauthorized requests are a sign of an expired - // jwt token, and that we've overrun the available window - // to download the actual file. Re-downloading the item - // will refresh that download url. - di, diErr := getDriveItem(ctx, oc.service, oc.driveID, itemID) - if diErr != nil { - err = errors.Wrap(diErr, "retrieving expired item") + for i := 1; i <= maxRetries; i++ { + _, itemData, err = oc.itemReader(oc.itemClient, item) + if err == nil { break } - item = di + if graph.IsErrUnauthorized(err) { + // assume unauthorized requests are a sign of an expired + // jwt token, and that we've overrun the available window + // to download the actual file. Re-downloading the item + // will refresh that download url. + di, diErr := getDriveItem(ctx, oc.service, oc.driveID, itemID) + if diErr != nil { + err = errors.Wrap(diErr, "retrieving expired item") + break + } - continue + item = di - } else if !graph.IsErrTimeout(err) && - !graph.IsInternalServerError(err) { - // Don't retry for non-timeout, on-unauth, as - // we are already retrying it in the default - // retry middleware - break + continue + + } else if !graph.IsErrTimeout(err) && + !graph.IsInternalServerError(err) { + // Don't retry for non-timeout, on-unauth, as + // we are already retrying it in the default + // retry middleware + break + } + + if i < maxRetries { + time.Sleep(1 * time.Second) + } } - if i < maxRetries { - time.Sleep(1 * time.Second) + // check for errors following retries + if err != nil { + errUpdater(itemID, err) + return nil, err } + + // display/log the item download + progReader, closer := observe.ItemProgress( + ctx, + itemData, + observe.ItemBackupMsg, + observe.PII(itemName+dataSuffix), + itemSize, + ) + go closer() + + return progReader, nil + }) + + oc.data <- &Item{ + id: itemName + dataSuffix, + data: itemReader, + info: itemInfo, } + } - // check for errors following retries - if err != nil { - errUpdater(itemID, err) - return nil, err + if oc.source == OneDriveSource { + metaReader := lazy.NewLazyReadCloser(func() (io.ReadCloser, error) { + progReader, closer := observe.ItemProgress( + ctx, itemMeta, observe.ItemBackupMsg, + observe.PII(itemName+metaSuffix), int64(itemMetaSize)) + go closer() + return progReader, nil + }) + + oc.data <- &Item{ + id: itemName + metaSuffix, + data: metaReader, + info: itemInfo, } + } - // display/log the item download - progReader, closer := observe.ItemProgress(ctx, itemData, observe.ItemBackupMsg, observe.PII(itemName), itemSize) - go closer() - - return progReader, nil - }) - - // This can cause inaccurate counts. Right now it counts all the items - // we intend to read. Errors within the lazy readCloser will create a - // conflict: an item is both successful and erroneous. But the async - // control to fix that is more error-prone than helpful. - // - // TODO: transform this into a stats bus so that async control of stats - // aggregation is handled at the backup level, not at the item iteration - // level. - // // Item read successfully, add to collection - atomic.AddInt64(&itemsRead, 1) + if isFile { + atomic.AddInt64(&itemsRead, 1) + } else { + atomic.AddInt64(&dirsRead, 1) + } + // byteCount iteration atomic.AddInt64(&byteCount, itemSize) - oc.data <- &Item{ - id: itemName, - data: itemReader, - info: itemInfo, - } folderProgress <- struct{}{} }(item) } wg.Wait() - oc.reportAsCompleted(ctx, int(itemsRead), byteCount, errs) + oc.reportAsCompleted(ctx, int(itemsFound), int(itemsRead), byteCount, errs) } -func (oc *Collection) reportAsCompleted(ctx context.Context, itemsRead int, byteCount int64, errs error) { +func (oc *Collection) reportAsCompleted(ctx context.Context, itemsFound, itemsRead int, byteCount int64, errs error) { close(oc.data) status := support.CreateStatus(ctx, support.Backup, 1, // num folders (always 1) support.CollectionMetrics{ - Objects: len(oc.driveItems), // items to read, - Successes: itemsRead, // items read successfully, - TotalBytes: byteCount, // Number of bytes read in the operation, + Objects: itemsFound, // items to read, + Successes: itemsRead, // items read successfully, + TotalBytes: byteCount, // Number of bytes read in the operation, }, errs, oc.folderPath.Folder(), // Additional details diff --git a/src/internal/connector/onedrive/collection_test.go b/src/internal/connector/onedrive/collection_test.go index b608e9068..b8e5fe446 100644 --- a/src/internal/connector/onedrive/collection_test.go +++ b/src/internal/connector/onedrive/collection_test.go @@ -2,8 +2,11 @@ package onedrive import ( "bytes" + "context" + "encoding/json" "io" "net/http" + "strings" "sync" "testing" "time" @@ -60,6 +63,14 @@ func (suite *CollectionUnitTestSuite) TestCollection() { testItemName = "itemName" testItemData = []byte("testdata") now = time.Now() + testItemMeta = Metadata{Permissions: []UserPermission{ + { + ID: "testMetaID", + Roles: []string{"read", "write"}, + Email: "email@provider.com", + Expiration: &now, + }, + }} ) type nst struct { @@ -164,6 +175,7 @@ func (suite *CollectionUnitTestSuite) TestCollection() { // Set a item reader, add an item and validate we get the item back mockItem := models.NewDriveItem() mockItem.SetId(&testItemID) + mockItem.SetFile(models.NewFile()) mockItem.SetName(&test.itemDeets.name) mockItem.SetSize(&test.itemDeets.size) mockItem.SetCreatedDateTime(&test.itemDeets.time) @@ -174,6 +186,18 @@ func (suite *CollectionUnitTestSuite) TestCollection() { } coll.itemReader = test.itemReader + coll.itemMetaReader = func(_ context.Context, + _ graph.Servicer, + _ string, + _ models.DriveItemable, + ) (io.ReadCloser, int, error) { + metaJSON, err := json.Marshal(testItemMeta) + if err != nil { + return nil, 0, err + } + + return io.NopCloser(bytes.NewReader(metaJSON)), len(metaJSON), nil + } // Read items from the collection wg.Add(1) @@ -184,28 +208,54 @@ func (suite *CollectionUnitTestSuite) TestCollection() { wg.Wait() + if test.source == OneDriveSource { + require.Len(t, readItems, 2) // .data and .meta + } else { + require.Len(t, readItems, 1) + } + + // Expect only 1 item + require.Equal(t, 1, collStatus.ObjectCount) + require.Equal(t, 1, collStatus.Successful) + // Validate item info and data readItem := readItems[0] readItemInfo := readItem.(data.StreamInfo) - readData, err := io.ReadAll(readItem.ToReader()) - require.NoError(t, err) - assert.Equal(t, testItemData, readData) - - // Expect only 1 item - require.Len(t, readItems, 1) - require.Equal(t, 1, collStatus.ObjectCount, "items iterated") - require.Equal(t, 1, collStatus.Successful, "items successful") - - assert.Equal(t, testItemName, readItem.UUID()) + if test.source == OneDriveSource { + assert.Equal(t, testItemName+DataFileSuffix, readItem.UUID()) + } else { + assert.Equal(t, testItemName, readItem.UUID()) + } require.Implements(t, (*data.StreamModTime)(nil), readItem) mt := readItem.(data.StreamModTime) assert.Equal(t, now, mt.ModTime()) + readData, err := io.ReadAll(readItem.ToReader()) + require.NoError(t, err) + name, parentPath := test.infoFrom(t, readItemInfo.Info()) + + assert.Equal(t, testItemData, readData) assert.Equal(t, testItemName, name) assert.Equal(t, driveFolderPath, parentPath) + + if test.source == OneDriveSource { + readItemMeta := readItems[1] + + assert.Equal(t, testItemName+MetaFileSuffix, readItemMeta.UUID()) + + readMetaData, err := io.ReadAll(readItemMeta.ToReader()) + require.NoError(t, err) + + tm, err := json.Marshal(testItemMeta) + if err != nil { + t.Fatal("unable to marshall test permissions", err) + } + + assert.Equal(t, tm, readMetaData) + } }) } } @@ -255,6 +305,7 @@ func (suite *CollectionUnitTestSuite) TestCollectionReadError() { mockItem := models.NewDriveItem() mockItem.SetId(&testItemID) + mockItem.SetFile(models.NewFile()) mockItem.SetName(&name) mockItem.SetSize(&size) mockItem.SetCreatedDateTime(&now) @@ -265,6 +316,14 @@ func (suite *CollectionUnitTestSuite) TestCollectionReadError() { return details.ItemInfo{}, nil, assert.AnError } + coll.itemMetaReader = func(_ context.Context, + _ graph.Servicer, + _ string, + _ models.DriveItemable, + ) (io.ReadCloser, int, error) { + return io.NopCloser(strings.NewReader(`{}`)), 2, nil + } + collItem, ok := <-coll.Items() assert.True(t, ok) @@ -279,3 +338,87 @@ func (suite *CollectionUnitTestSuite) TestCollectionReadError() { }) } } + +func (suite *CollectionUnitTestSuite) TestCollectionDisablePermissionsBackup() { + table := []struct { + name string + source driveSource + }{ + { + name: "oneDrive", + source: OneDriveSource, + }, + } + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + var ( + testItemID = "fakeItemID" + testItemName = "Fake Item" + testItemSize = int64(10) + + collStatus = support.ConnectorOperationStatus{} + wg = sync.WaitGroup{} + ) + + wg.Add(1) + + folderPath, err := GetCanonicalPath("drive/driveID1/root:/folderPath", "a-tenant", "a-user", test.source) + require.NoError(t, err) + + coll := NewCollection( + graph.HTTPClient(graph.NoTimeout()), + folderPath, + "fakeDriveID", + suite, + suite.testStatusUpdater(&wg, &collStatus), + test.source, + control.Options{ToggleFeatures: control.Toggles{DisablePermissionsBackup: true}}) + + now := time.Now() + mockItem := models.NewDriveItem() + mockItem.SetFile(models.NewFile()) + mockItem.SetId(&testItemID) + mockItem.SetName(&testItemName) + mockItem.SetSize(&testItemSize) + mockItem.SetCreatedDateTime(&now) + mockItem.SetLastModifiedDateTime(&now) + coll.Add(mockItem) + + coll.itemReader = func( + *http.Client, + models.DriveItemable, + ) (details.ItemInfo, io.ReadCloser, error) { + return details.ItemInfo{OneDrive: &details.OneDriveInfo{ItemName: "fakeName", Modified: time.Now()}}, + io.NopCloser(strings.NewReader("Fake Data!")), + nil + } + + coll.itemMetaReader = func(_ context.Context, + _ graph.Servicer, + _ string, + _ models.DriveItemable, + ) (io.ReadCloser, int, error) { + return io.NopCloser(strings.NewReader(`{"key": "value"}`)), 16, nil + } + + readItems := []data.Stream{} + for item := range coll.Items() { + readItems = append(readItems, item) + } + + wg.Wait() + + // Expect no items + require.Equal(t, 1, collStatus.ObjectCount) + require.Equal(t, 1, collStatus.Successful) + + for _, i := range readItems { + if strings.HasSuffix(i.UUID(), MetaFileSuffix) { + content, err := io.ReadAll(i.ToReader()) + require.NoError(t, err) + require.Equal(t, content, []byte("{}")) + } + } + }) + } +} diff --git a/src/internal/connector/onedrive/collections.go b/src/internal/connector/onedrive/collections.go index 200e51e23..50c5323d9 100644 --- a/src/internal/connector/onedrive/collections.go +++ b/src/internal/connector/onedrive/collections.go @@ -430,6 +430,12 @@ func (c *Collections) UpdateCollections( // already created and partially populated. updatePath(newPaths, *item.GetId(), folderPath.String()) + if c.source != OneDriveSource { + continue + } + + fallthrough + case item.GetFile() != nil: if item.GetDeleted() != nil { excluded[*item.GetId()] = struct{}{} @@ -445,6 +451,7 @@ func (c *Collections) UpdateCollections( // the exclude list. col, found := c.CollectionMap[collectionPath.String()] + if !found { // TODO(ashmrtn): Compare old and new path and set collection state // accordingly. @@ -459,13 +466,17 @@ func (c *Collections) UpdateCollections( c.CollectionMap[collectionPath.String()] = col c.NumContainers++ - c.NumItems++ } collection := col.(*Collection) collection.Add(item) - c.NumFiles++ + c.NumItems++ + if item.GetFile() != nil { + // This is necessary as we have a fallthrough for + // folders and packages + c.NumFiles++ + } default: return errors.Errorf("item type not supported. item name : %s", *item.GetName()) diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index 21dae9549..3316a10c5 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -139,7 +139,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { user, testBaseDrivePath, ), - expectedItemCount: 2, + expectedItemCount: 1, expectedFileCount: 1, expectedContainerCount: 1, // Root folder is skipped since it's always present. @@ -151,10 +151,15 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { items: []models.DriveItemable{ driveItem("folder", "folder", testBaseDrivePath, false, true, false), }, - inputFolderMap: map[string]string{}, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, + inputFolderMap: map[string]string{}, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), expectedMetadataPaths: map[string]string{ "folder": expectedPathAsSlice( suite.T(), @@ -163,17 +168,24 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/folder", )[0], }, - expectedExcludes: map[string]struct{}{}, + expectedItemCount: 1, + expectedContainerCount: 1, + expectedExcludes: map[string]struct{}{}, }, { testCase: "Single Package", items: []models.DriveItemable{ driveItem("package", "package", testBaseDrivePath, false, false, true), }, - inputFolderMap: map[string]string{}, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, + inputFolderMap: map[string]string{}, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), expectedMetadataPaths: map[string]string{ "package": expectedPathAsSlice( suite.T(), @@ -182,7 +194,9 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/package", )[0], }, - expectedExcludes: map[string]struct{}{}, + expectedItemCount: 1, + expectedContainerCount: 1, + expectedExcludes: map[string]struct{}{}, }, { testCase: "1 root file, 1 folder, 1 package, 2 files, 3 collections", @@ -204,7 +218,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+folder, testBaseDrivePath+pkg, ), - expectedItemCount: 6, + expectedItemCount: 5, expectedFileCount: 3, expectedContainerCount: 3, expectedMetadataPaths: map[string]string{ @@ -238,23 +252,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { inputFolderMap: map[string]string{}, scope: (&selectors.OneDriveBackup{}).Folders([]string{"folder"})[0], expect: assert.NoError, - expectedCollectionPaths: append( - expectedPathAsSlice( - suite.T(), - tenant, - user, - testBaseDrivePath+"/folder", - ), - expectedPathAsSlice( - suite.T(), - tenant, - user, - testBaseDrivePath+folderSub+folder, - )..., + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath+"/folder", + testBaseDrivePath+folderSub, + testBaseDrivePath+folderSub+folder, ), expectedItemCount: 4, expectedFileCount: 2, - expectedContainerCount: 2, + expectedContainerCount: 3, // just "folder" isn't added here because the include check is done on the // parent path since we only check later if something is a folder or not. expectedMetadataPaths: map[string]string{ @@ -293,11 +301,12 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { suite.T(), tenant, user, + testBaseDrivePath+folderSub, testBaseDrivePath+folderSub+folder, ), expectedItemCount: 2, expectedFileCount: 1, - expectedContainerCount: 1, + expectedContainerCount: 2, expectedMetadataPaths: map[string]string{ "folder2": expectedPathAsSlice( suite.T(), @@ -328,7 +337,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { user, testBaseDrivePath+folderSub, ), - expectedItemCount: 2, + expectedItemCount: 1, expectedFileCount: 1, expectedContainerCount: 1, // No child folders for subfolder so nothing here. @@ -354,12 +363,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/folder/subfolder", )[0], }, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, - expectedItemCount: 0, - expectedFileCount: 0, - expectedContainerCount: 0, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), + expectedItemCount: 1, + expectedFileCount: 0, + expectedContainerCount: 1, expectedMetadataPaths: map[string]string{ "folder": expectedPathAsSlice( suite.T(), @@ -395,12 +409,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/a-folder/subfolder", )[0], }, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, - expectedItemCount: 0, - expectedFileCount: 0, - expectedContainerCount: 0, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), + expectedItemCount: 1, + expectedFileCount: 0, + expectedContainerCount: 1, expectedMetadataPaths: map[string]string{ "folder": expectedPathAsSlice( suite.T(), @@ -437,12 +456,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/a-folder/subfolder", )[0], }, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, - expectedItemCount: 0, - expectedFileCount: 0, - expectedContainerCount: 0, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), + expectedItemCount: 2, + expectedFileCount: 0, + expectedContainerCount: 1, expectedMetadataPaths: map[string]string{ "folder": expectedPathAsSlice( suite.T(), @@ -479,12 +503,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/a-folder/subfolder", )[0], }, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, - expectedItemCount: 0, - expectedFileCount: 0, - expectedContainerCount: 0, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), + expectedItemCount: 2, + expectedFileCount: 0, + expectedContainerCount: 1, expectedMetadataPaths: map[string]string{ "folder": expectedPathAsSlice( suite.T(), @@ -550,12 +579,17 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testBaseDrivePath+"/folder/subfolder", )[0], }, - scope: anyFolder, - expect: assert.NoError, - expectedCollectionPaths: []string{}, - expectedItemCount: 0, - expectedFileCount: 0, - expectedContainerCount: 0, + scope: anyFolder, + expect: assert.NoError, + expectedCollectionPaths: expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + ), + expectedItemCount: 1, + expectedFileCount: 0, + expectedContainerCount: 1, expectedMetadataPaths: map[string]string{ "subfolder": expectedPathAsSlice( suite.T(), @@ -1043,6 +1077,12 @@ func (suite *OneDriveCollectionsSuite) TestGet() { ) require.NoError(suite.T(), err, "making metadata path") + rootFolderPath := expectedPathAsSlice( + suite.T(), + tenant, + user, + testBaseDrivePath, + )[0] folderPath := expectedPathAsSlice( suite.T(), tenant, @@ -1067,6 +1107,12 @@ func (suite *OneDriveCollectionsSuite) TestGet() { driveBasePath2 := "drive/driveID2/root:" + rootFolderPath2 := expectedPathAsSlice( + suite.T(), + tenant, + user, + driveBasePath2, + )[0] folderPath2 := expectedPathAsSlice( suite.T(), tenant, @@ -1161,7 +1207,8 @@ func (suite *OneDriveCollectionsSuite) TestGet() { }, errCheck: assert.NoError, expectedCollections: map[string][]string{ - folderPath: {"file"}, + folderPath: {"file"}, + rootFolderPath: {"folder"}, }, expectedDeltaURLs: map[string]string{ driveID1: delta, @@ -1189,7 +1236,8 @@ func (suite *OneDriveCollectionsSuite) TestGet() { }, errCheck: assert.NoError, expectedCollections: map[string][]string{ - folderPath: {"file"}, + folderPath: {"file"}, + rootFolderPath: {"folder"}, }, expectedDeltaURLs: map[string]string{}, expectedFolderPaths: map[string]map[string]string{}, @@ -1218,7 +1266,8 @@ func (suite *OneDriveCollectionsSuite) TestGet() { }, errCheck: assert.NoError, expectedCollections: map[string][]string{ - folderPath: {"file", "file2"}, + folderPath: {"file", "file2"}, + rootFolderPath: {"folder"}, }, expectedDeltaURLs: map[string]string{ driveID1: delta, @@ -1258,8 +1307,10 @@ func (suite *OneDriveCollectionsSuite) TestGet() { }, errCheck: assert.NoError, expectedCollections: map[string][]string{ - folderPath: {"file"}, - folderPath2: {"file"}, + folderPath: {"file"}, + folderPath2: {"file"}, + rootFolderPath: {"folder"}, + rootFolderPath2: {"folder"}, }, expectedDeltaURLs: map[string]string{ driveID1: delta, diff --git a/src/internal/connector/onedrive/item.go b/src/internal/connector/onedrive/item.go index b1027de9d..1526f1401 100644 --- a/src/internal/connector/onedrive/item.go +++ b/src/internal/connector/onedrive/item.go @@ -1,7 +1,9 @@ package onedrive import ( + "bytes" "context" + "encoding/json" "fmt" "io" "net/http" @@ -37,6 +39,7 @@ func getDriveItem( // sharePointItemReader will return a io.ReadCloser for the specified item // It crafts this by querying M365 for a download URL for the item // and using a http client to initialize a reader +// TODO: Add metadata fetching to SharePoint func sharePointItemReader( hc *http.Client, item models.DriveItemable, @@ -53,6 +56,25 @@ func sharePointItemReader( return dii, resp.Body, nil } +func oneDriveItemMetaReader( + ctx context.Context, + service graph.Servicer, + driveID string, + item models.DriveItemable, +) (io.ReadCloser, int, error) { + meta, err := oneDriveItemMetaInfo(ctx, service, driveID, item) + if err != nil { + return nil, 0, err + } + + metaJSON, err := json.Marshal(meta) + if err != nil { + return nil, 0, err + } + + return io.NopCloser(bytes.NewReader(metaJSON)), len(metaJSON), nil +} + // oneDriveItemReader will return a io.ReadCloser for the specified item // It crafts this by querying M365 for a download URL for the item // and using a http client to initialize a reader @@ -60,16 +82,25 @@ func oneDriveItemReader( hc *http.Client, item models.DriveItemable, ) (details.ItemInfo, io.ReadCloser, error) { - resp, err := downloadItem(hc, item) - if err != nil { - return details.ItemInfo{}, nil, errors.Wrap(err, "downloading item") + var ( + rc io.ReadCloser + isFile = item.GetFile() != nil + ) + + if isFile { + resp, err := downloadItem(hc, item) + if err != nil { + return details.ItemInfo{}, nil, errors.Wrap(err, "downloading item") + } + + rc = resp.Body } dii := details.ItemInfo{ OneDrive: oneDriveItemInfo(item, *item.GetSize()), } - return dii, resp.Body, nil + return dii, rc, nil } func downloadItem(hc *http.Client, item models.DriveItemable) (*http.Response, error) { @@ -149,6 +180,47 @@ func oneDriveItemInfo(di models.DriveItemable, itemSize int64) *details.OneDrive } } +// oneDriveItemMetaInfo will fetch the meta information for a drive +// item. As of now, it only adds the permissions applicable for a +// onedrive item. +func oneDriveItemMetaInfo( + ctx context.Context, service graph.Servicer, + driveID string, di models.DriveItemable, +) (Metadata, error) { + itemID := di.GetId() + + perm, err := service.Client().DrivesById(driveID).ItemsById(*itemID).Permissions().Get(ctx, nil) + if err != nil { + return Metadata{}, errors.Wrapf(err, "failed to get item permissions %s", *itemID) + } + + up := []UserPermission{} + + for _, p := range perm.GetValue() { + roles := []string{} + + for _, r := range p.GetRoles() { + // Skip if the only role available in owner + if r != "owner" { + roles = append(roles, r) + } + } + + if len(roles) == 0 { + continue + } + + up = append(up, UserPermission{ + ID: *p.GetId(), + Roles: roles, + Email: *p.GetGrantedToV2().GetUser().GetAdditionalData()["email"].(*string), + Expiration: p.GetExpirationDateTime(), + }) + } + + return Metadata{Permissions: up}, nil +} + // sharePointItemInfo will populate a details.SharePointInfo struct // with properties from the drive item. ItemSize is specified // separately for restore processes because the local itemable diff --git a/src/internal/connector/onedrive/item_test.go b/src/internal/connector/onedrive/item_test.go index 6a8894ebf..b0e42943a 100644 --- a/src/internal/connector/onedrive/item_test.go +++ b/src/internal/connector/onedrive/item_test.go @@ -138,8 +138,8 @@ func (suite *ItemIntegrationSuite) TestItemReader_oneDrive() { ) // Read data for the file - itemInfo, itemData, err := oneDriveItemReader(graph.HTTPClient(graph.NoTimeout()), driveItem) + require.NoError(suite.T(), err) require.NotNil(suite.T(), itemInfo.OneDrive) require.NotEmpty(suite.T(), itemInfo.OneDrive.ItemName) diff --git a/src/internal/connector/onedrive/restore.go b/src/internal/connector/onedrive/restore.go index 00ed855b7..af591cd86 100644 --- a/src/internal/connector/onedrive/restore.go +++ b/src/internal/connector/onedrive/restore.go @@ -2,9 +2,15 @@ package onedrive import ( "context" + "encoding/json" + "fmt" "io" "runtime/trace" + "sort" + "strings" + msdrive "github.com/microsoftgraph/msgraph-sdk-go/drive" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/connector/graph" @@ -25,28 +31,92 @@ const ( copyBufferSize = 5 * 1024 * 1024 ) +func getParentPermissions( + parentPath path.Path, + parentPermissions map[string][]UserPermission, +) ([]UserPermission, error) { + parentPerms, ok := parentPermissions[parentPath.String()] + if !ok { + onedrivePath, err := path.ToOneDrivePath(parentPath) + if err != nil { + return nil, errors.Wrap(err, "invalid restore path") + } + + if len(onedrivePath.Folders) != 0 { + return nil, errors.Wrap(err, "unable to compute item permissions") + } + + parentPerms = []UserPermission{} + } + + return parentPerms, nil +} + // RestoreCollections will restore the specified data collections into OneDrive func RestoreCollections( ctx context.Context, service graph.Servicer, dest control.RestoreDestination, + opts control.Options, dcs []data.Collection, deets *details.Builder, ) (*support.ConnectorOperationStatus, error) { var ( restoreMetrics support.CollectionMetrics restoreErrors error + metrics support.CollectionMetrics + folderPerms map[string][]UserPermission + canceled bool + + // permissionIDMappings is used to map between old and new id + // of permissions as we restore them + permissionIDMappings = map[string]string{} ) errUpdater := func(id string, err error) { restoreErrors = support.WrapAndAppend(id, err, restoreErrors) } + // Reorder collections so that the parents directories are created + // before the child directories + sort.Slice(dcs, func(i, j int) bool { + return dcs[i].FullPath().String() < dcs[j].FullPath().String() + }) + + parentPermissions := map[string][]UserPermission{} + // Iterate through the data collections and restore the contents of each for _, dc := range dcs { - temp, canceled := RestoreCollection(ctx, service, dc, OneDriveSource, dest.ContainerName, deets, errUpdater) + var ( + parentPerms []UserPermission + err error + ) - restoreMetrics.Combine(temp) + if opts.RestorePermissions { + parentPerms, err = getParentPermissions(dc.FullPath(), parentPermissions) + if err != nil { + errUpdater(dc.FullPath().String(), err) + } + } + + metrics, folderPerms, permissionIDMappings, canceled = RestoreCollection( + ctx, + service, + dc, + parentPerms, + OneDriveSource, + dest.ContainerName, + deets, + errUpdater, + permissionIDMappings, + opts.RestorePermissions, + ) + + for k, v := range folderPerms { + parentPermissions[k] = v + } + + restoreMetrics.Combine(metrics) if canceled { break @@ -66,29 +136,36 @@ func RestoreCollections( // RestoreCollection handles restoration of an individual collection. // returns: // - the collection's item and byte count metrics -// - the context cancellation state (true if the context is cancelled) +// - the context cancellation state (true if the context is canceled) func RestoreCollection( ctx context.Context, service graph.Servicer, dc data.Collection, + parentPerms []UserPermission, source driveSource, restoreContainerName string, deets *details.Builder, errUpdater func(string, error), -) (support.CollectionMetrics, bool) { + permissionIDMappings map[string]string, + restorePerms bool, +) (support.CollectionMetrics, map[string][]UserPermission, map[string]string, bool) { ctx, end := D.Span(ctx, "gc:oneDrive:restoreCollection", D.Label("path", dc.FullPath())) defer end() var ( - metrics = support.CollectionMetrics{} - copyBuffer = make([]byte, copyBufferSize) - directory = dc.FullPath() + metrics = support.CollectionMetrics{} + copyBuffer = make([]byte, copyBufferSize) + directory = dc.FullPath() + restoredIDs = map[string]string{} + itemInfo details.ItemInfo + itemID string + folderPerms = map[string][]UserPermission{} ) drivePath, err := path.ToOneDrivePath(directory) if err != nil { errUpdater(directory.String(), err) - return metrics, false + return metrics, folderPerms, permissionIDMappings, false } // Assemble folder hierarchy we're going to restore into (we recreate the folder hierarchy @@ -108,7 +185,7 @@ func RestoreCollection( restoreFolderID, err := CreateRestoreFolders(ctx, service, drivePath.DriveID, restoreFolderElements) if err != nil { errUpdater(directory.String(), errors.Wrapf(err, "failed to create folders %v", restoreFolderElements)) - return metrics, false + return metrics, folderPerms, permissionIDMappings, false } // Restore items from the collection @@ -118,50 +195,175 @@ func RestoreCollection( select { case <-ctx.Done(): errUpdater("context canceled", ctx.Err()) - return metrics, true + return metrics, folderPerms, permissionIDMappings, true case itemData, ok := <-items: if !ok { - return metrics, false - } - metrics.Objects++ - - metrics.TotalBytes += int64(len(copyBuffer)) - - itemInfo, err := restoreItem(ctx, - service, - itemData, - drivePath.DriveID, - restoreFolderID, - copyBuffer, - source) - if err != nil { - errUpdater(itemData.UUID(), err) - continue + return metrics, folderPerms, permissionIDMappings, false } itemPath, err := dc.FullPath().Append(itemData.UUID(), true) if err != nil { logger.Ctx(ctx).DPanicw("transforming item to full path", "error", err) + errUpdater(itemData.UUID(), err) continue } - deets.Add( - itemPath.String(), - itemPath.ShortRef(), - "", - true, - itemInfo) + if source == OneDriveSource { + name := itemData.UUID() + if strings.HasSuffix(name, DataFileSuffix) { + metrics.Objects++ + metrics.TotalBytes += int64(len(copyBuffer)) + trimmedName := strings.TrimSuffix(name, DataFileSuffix) - metrics.Successes++ + itemID, itemInfo, err = restoreData(ctx, service, trimmedName, itemData, + drivePath.DriveID, restoreFolderID, copyBuffer, source) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + restoredIDs[trimmedName] = itemID + + deets.Add(itemPath.String(), itemPath.ShortRef(), "", true, itemInfo) + + // Mark it as success without processing .meta + // file if we are not restoring permissions + if !restorePerms { + metrics.Successes++ + } + } else if strings.HasSuffix(name, MetaFileSuffix) { + if !restorePerms { + continue + } + + meta, err := getMetadata(itemData.ToReader()) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + trimmedName := strings.TrimSuffix(name, MetaFileSuffix) + restoreID, ok := restoredIDs[trimmedName] + if !ok { + errUpdater(itemData.UUID(), fmt.Errorf("item not available to restore permissions")) + continue + } + + permissionIDMappings, err = restorePermissions( + ctx, + service, + drivePath.DriveID, + restoreID, + parentPerms, + meta.Permissions, + permissionIDMappings, + ) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + // Objects count is incremented when we restore a + // data file and success count is incremented when + // we restore a meta file as every data file + // should have an associated meta file + metrics.Successes++ + } else if strings.HasSuffix(name, DirMetaFileSuffix) { + trimmedName := strings.TrimSuffix(name, DirMetaFileSuffix) + folderID, err := createRestoreFolder( + ctx, + service, + drivePath.DriveID, + trimmedName, + restoreFolderID, + ) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + if !restorePerms { + continue + } + + meta, err := getMetadata(itemData.ToReader()) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + permissionIDMappings, err = restorePermissions( + ctx, + service, + drivePath.DriveID, + folderID, + parentPerms, + meta.Permissions, + permissionIDMappings, + ) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + trimmedPath := strings.TrimSuffix(itemPath.String(), DirMetaFileSuffix) + folderPerms[trimmedPath] = meta.Permissions + } else { + if !ok { + errUpdater(itemData.UUID(), fmt.Errorf("invalid backup format, you might be using an old backup")) + continue + } + } + } else { + metrics.Objects++ + metrics.TotalBytes += int64(len(copyBuffer)) + + // No permissions stored at the moment for SharePoint + _, itemInfo, err = restoreData(ctx, + service, + itemData.UUID(), + itemData, + drivePath.DriveID, + restoreFolderID, + copyBuffer, + source) + if err != nil { + errUpdater(itemData.UUID(), err) + continue + } + + deets.Add(itemPath.String(), itemPath.ShortRef(), "", true, itemInfo) + metrics.Successes++ + } } } } -// createRestoreFolders creates the restore folder hieararchy in the specified drive and returns the folder ID -// of the last folder entry in the hiearchy +// Creates a folder with its permissions +func createRestoreFolder( + ctx context.Context, + service graph.Servicer, + driveID, folder, parentFolderID string, +) (string, error) { + folderItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(folder, true)) + if err != nil { + return "", errors.Wrapf( + err, + "failed to create folder %s/%s. details: %s", parentFolderID, folder, + support.ConnectorStackErrorTrace(err), + ) + } + + logger.Ctx(ctx).Debugf("Resolved %s in %s to %s", folder, parentFolderID, *folderItem.GetId()) + + return *folderItem.GetId(), nil +} + +// createRestoreFolders creates the restore folder hierarchy in the specified drive and returns the folder ID +// of the last folder entry in the hierarchy func CreateRestoreFolders(ctx context.Context, service graph.Servicer, driveID string, restoreFolders []string, ) (string, error) { driveRoot, err := service.Client().DrivesById(driveID).Root().Get(ctx, nil) @@ -209,15 +411,16 @@ func CreateRestoreFolders(ctx context.Context, service graph.Servicer, driveID s return parentFolderID, nil } -// restoreItem will create a new item in the specified `parentFolderID` and upload the data.Stream -func restoreItem( +// restoreData will create a new item in the specified `parentFolderID` and upload the data.Stream +func restoreData( ctx context.Context, service graph.Servicer, + name string, itemData data.Stream, driveID, parentFolderID string, copyBuffer []byte, source driveSource, -) (details.ItemInfo, error) { +) (string, details.ItemInfo, error) { ctx, end := D.Span(ctx, "gc:oneDrive:restoreItem", D.Label("item_uuid", itemData.UUID())) defer end() @@ -227,19 +430,19 @@ func restoreItem( // Get the stream size (needed to create the upload session) ss, ok := itemData.(data.StreamSize) if !ok { - return details.ItemInfo{}, errors.Errorf("item %q does not implement DataStreamInfo", itemName) + return "", details.ItemInfo{}, errors.Errorf("item %q does not implement DataStreamInfo", itemName) } // Create Item - newItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(itemData.UUID(), false)) + newItem, err := createItem(ctx, service, driveID, parentFolderID, newItem(name, false)) if err != nil { - return details.ItemInfo{}, errors.Wrapf(err, "failed to create item %s", itemName) + return "", details.ItemInfo{}, errors.Wrapf(err, "failed to create item %s", itemName) } // Get a drive item writer w, err := driveItemWriter(ctx, service, driveID, *newItem.GetId(), ss.Size()) if err != nil { - return details.ItemInfo{}, errors.Wrapf(err, "failed to create item upload session %s", itemName) + return "", details.ItemInfo{}, errors.Wrapf(err, "failed to create item upload session %s", itemName) } iReader := itemData.ToReader() @@ -250,7 +453,7 @@ func restoreItem( // Upload the stream data written, err := io.CopyBuffer(w, progReader, copyBuffer) if err != nil { - return details.ItemInfo{}, errors.Wrapf(err, "failed to upload data: item %s", itemName) + return "", details.ItemInfo{}, errors.Wrapf(err, "failed to upload data: item %s", itemName) } dii := details.ItemInfo{} @@ -262,5 +465,129 @@ func restoreItem( dii.OneDrive = oneDriveItemInfo(newItem, written) } - return dii, nil + return *newItem.GetId(), dii, nil +} + +// getMetadata read and parses the metadata info for an item +func getMetadata(metar io.ReadCloser) (Metadata, error) { + var meta Metadata + // `metar` will be nil for the top level container folder + if metar != nil { + metaraw, err := io.ReadAll(metar) + if err != nil { + return Metadata{}, err + } + + err = json.Unmarshal(metaraw, &meta) + if err != nil { + return Metadata{}, err + } + } + + return meta, nil +} + +// getChildPermissions is to filter out permissions present in the +// parent from the ones that are available for child. This is +// necessary as we store the nested permissions in the child. We +// cannot avoid storing the nested permissions as it is possible that +// a file in a folder can remove the nested permission that is present +// on itself. +func getChildPermissions(childPermissions, parentPermissions []UserPermission) ([]UserPermission, []UserPermission) { + addedPermissions := []UserPermission{} + removedPermissions := []UserPermission{} + + for _, cp := range childPermissions { + found := false + + for _, pp := range parentPermissions { + if cp.ID == pp.ID { + found = true + break + } + } + + if !found { + addedPermissions = append(addedPermissions, cp) + } + } + + for _, pp := range parentPermissions { + found := false + + for _, cp := range childPermissions { + if pp.ID == cp.ID { + found = true + break + } + } + + if !found { + removedPermissions = append(removedPermissions, pp) + } + } + + return addedPermissions, removedPermissions +} + +// restorePermissions takes in the permissions that were added and the +// removed(ones present in parent but not in child) and adds/removes +// the necessary permissions on onedrive objects. +func restorePermissions( + ctx context.Context, + service graph.Servicer, + driveID string, + itemID string, + parentPerms []UserPermission, + childPerms []UserPermission, + permissionIDMappings map[string]string, +) (map[string]string, error) { + permAdded, permRemoved := getChildPermissions(childPerms, parentPerms) + + for _, p := range permRemoved { + err := service.Client().DrivesById(driveID).ItemsById(itemID). + PermissionsById(permissionIDMappings[p.ID]).Delete(ctx, nil) + if err != nil { + return permissionIDMappings, errors.Wrapf( + err, + "failed to remove permission for item %s. details: %s", + itemID, + support.ConnectorStackErrorTrace(err), + ) + } + } + + for _, p := range permAdded { + pbody := msdrive.NewItemsItemInvitePostRequestBody() + pbody.SetRoles(p.Roles) + + if p.Expiration != nil { + expiry := p.Expiration.String() + pbody.SetExpirationDateTime(&expiry) + } + + si := false + pbody.SetSendInvitation(&si) + + rs := true + pbody.SetRequireSignIn(&rs) + + rec := models.NewDriveRecipient() + rec.SetEmail(&p.Email) + pbody.SetRecipients([]models.DriveRecipientable{rec}) + + np, err := service.Client().DrivesById(driveID).ItemsById(itemID).Invite().Post(ctx, pbody, nil) + if err != nil { + return permissionIDMappings, errors.Wrapf( + err, + "failed to set permission for item %s. details: %s", + itemID, + support.ConnectorStackErrorTrace(err), + ) + } + + permissionIDMappings[p.ID] = *np.GetValue()[0].GetId() + } + + return permissionIDMappings, nil } diff --git a/src/internal/connector/sharepoint/data_collections_test.go b/src/internal/connector/sharepoint/data_collections_test.go index 87aaa5c84..11d05156c 100644 --- a/src/internal/connector/sharepoint/data_collections_test.go +++ b/src/internal/connector/sharepoint/data_collections_test.go @@ -77,7 +77,7 @@ func (suite *SharePointLibrariesSuite) TestUpdateCollections() { site, testBaseDrivePath, ), - expectedItemCount: 2, + expectedItemCount: 1, expectedFileCount: 1, expectedContainerCount: 1, }, diff --git a/src/internal/connector/sharepoint/restore.go b/src/internal/connector/sharepoint/restore.go index ef2b940bb..4784ed209 100644 --- a/src/internal/connector/sharepoint/restore.go +++ b/src/internal/connector/sharepoint/restore.go @@ -59,14 +59,18 @@ func RestoreCollections( switch dc.FullPath().Category() { case path.LibrariesCategory: - metrics, canceled = onedrive.RestoreCollection( + metrics, _, _, canceled = onedrive.RestoreCollection( ctx, service, dc, + []onedrive.UserPermission{}, // Currently permission data is not stored for sharepoint onedrive.OneDriveSource, dest.ContainerName, deets, - errUpdater) + errUpdater, + map[string]string{}, + false, + ) case path.ListsCategory: metrics, canceled = RestoreCollection( ctx, diff --git a/src/internal/connector/support/status.go b/src/internal/connector/support/status.go index 7e38758d3..dcf5f32c5 100644 --- a/src/internal/connector/support/status.go +++ b/src/internal/connector/support/status.go @@ -66,6 +66,7 @@ func CreateStatus( hasErrors := err != nil numErr := GetNumberOfErrors(err) + status := ConnectorOperationStatus{ lastOperation: op, ObjectCount: cm.Objects, diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index 21d4009e0..a57a9d2be 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -339,7 +339,7 @@ func generateContainerOfItems( dest, collections) - deets, err := gc.RestoreDataCollections(ctx, acct, sel, dest, dataColls) + deets, err := gc.RestoreDataCollections(ctx, acct, sel, dest, control.Options{RestorePermissions: true}, dataColls) require.NoError(t, err) return deets diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index aa9229336..206eb8026 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "runtime/debug" + "sort" "time" "github.com/alcionai/clues" @@ -221,7 +222,9 @@ func (op *RestoreOperation) do(ctx context.Context) (restoreDetails *details.Det op.account, op.Selectors, op.Destination, - dcs) + op.Options, + dcs, + ) if err != nil { opStats.writeErr = errors.Wrap(err, "restoring service data") return nil, opStats.writeErr @@ -327,6 +330,17 @@ func formatDetailsForRestoration( paths[i] = p } + // TODO(meain): Move this to onedrive specific component, but as + // of now the paths can technically be from multiple services + + // This sort is done primarily to order `.meta` files after `.data` + // files. This is only a necessity for OneDrive as we are storing + // metadata for files/folders in separate meta files and we the + // data to be restored before we can restore the metadata. + sort.Slice(paths, func(i, j int) bool { + return paths[i].String() < paths[j].String() + }) + if errs != nil { return nil, errs } diff --git a/src/pkg/control/options.go b/src/pkg/control/options.go index 9cc5a334a..6f53839ca 100644 --- a/src/pkg/control/options.go +++ b/src/pkg/control/options.go @@ -6,10 +6,11 @@ import ( // Options holds the optional configurations for a process type Options struct { - Collision CollisionPolicy `json:"-"` - DisableMetrics bool `json:"disableMetrics"` - FailFast bool `json:"failFast"` - ToggleFeatures Toggles `json:"ToggleFeatures"` + Collision CollisionPolicy `json:"-"` + DisableMetrics bool `json:"disableMetrics"` + FailFast bool `json:"failFast"` + RestorePermissions bool `json:"restorePermissions"` + ToggleFeatures Toggles `json:"ToggleFeatures"` } // Defaults provides an Options with the default values set. @@ -74,4 +75,9 @@ type Toggles struct { // DisableIncrementals prevents backups from using incremental lookups, // forcing a new, complete backup of all data regardless of prior state. DisableIncrementals bool `json:"exchangeIncrementals,omitempty"` + + // DisablePermissionsBackup is used to disable backups of item + // permissions. Permission metadata increases graph api call count, + // so disabling their retrieval when not needed is advised. + DisablePermissionsBackup bool `json:"disablePermissionsBackup,omitempty"` } From 9da0a7878b5d01cf7750a30ff3a4a9566a098b5e Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 3 Feb 2023 09:33:14 +0530 Subject: [PATCH 42/46] Backup versioning (#2324) ## Description Add backup format version information to the backups so that we can distinguish between backups which use a single file vs the ones that use both .data and .meta files. Overrides https://github.com/alcionai/corso/pull/2297. I've also set it against `main` so that the diff shows up properly. Ref: https://github.com/alcionai/corso/pull/2324#issuecomment-1409709118 ## Does this PR need a docs update or release note? - [x] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [ ] :no_entry: No ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * fixes https://github.com/alcionai/corso/issues/2230 * https://github.com/alcionai/corso/issues/2253 ## Test Plan - [x] :muscle: Manual (Tested manually, will add an e2e test in a followup) - [ ] :zap: Unit test - [ ] :green_heart: E2E --- CHANGELOG.md | 1 + src/cmd/factory/impl/common.go | 3 +- src/internal/connector/graph_connector.go | 5 +- .../connector/graph_connector_helper_test.go | 56 ++- .../connector/graph_connector_test.go | 378 +++++++++++++++++- src/internal/connector/onedrive/restore.go | 10 +- src/internal/connector/sharepoint/restore.go | 2 + .../operations/backup_integration_test.go | 10 +- src/internal/operations/restore.go | 1 + src/pkg/backup/backup.go | 6 + 10 files changed, 465 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33485d926..09d3845da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Increase page size preference for delta requests for Exchange to reduce number of roundtrips - OneDrive file/folder permissions can now be backed up and restored - Add `--restore-permissions` flag to toggle restoration of OneDrive permissions +- Add versions to backups so that we can understand/handle older backup formats ### Known Issues diff --git a/src/cmd/factory/impl/common.go b/src/cmd/factory/impl/common.go index 3ed3831fc..78a5dca0e 100644 --- a/src/cmd/factory/impl/common.go +++ b/src/cmd/factory/impl/common.go @@ -16,6 +16,7 @@ import ( "github.com/alcionai/corso/src/internal/connector/mockconnector" "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/pkg/account" + "github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/credentials" @@ -91,7 +92,7 @@ func generateAndRestoreItems( Infof(ctx, "Generating %d %s items in %s\n", howMany, cat, Destination) - return gc.RestoreDataCollections(ctx, acct, sel, dest, opts, dataColls) + return gc.RestoreDataCollections(ctx, backup.Version, acct, sel, dest, opts, dataColls) } // ------------------------------------------------------------------------------------------ diff --git a/src/internal/connector/graph_connector.go b/src/internal/connector/graph_connector.go index 370948639..def430f14 100644 --- a/src/internal/connector/graph_connector.go +++ b/src/internal/connector/graph_connector.go @@ -266,6 +266,7 @@ func (gc *GraphConnector) UnionSiteIDsAndWebURLs(ctx context.Context, ids, urls // SideEffect: gc.status is updated at the completion of operation func (gc *GraphConnector) RestoreDataCollections( ctx context.Context, + backupVersion int, acct account.Account, selector selectors.Selector, dest control.RestoreDestination, @@ -290,9 +291,9 @@ func (gc *GraphConnector) RestoreDataCollections( case selectors.ServiceExchange: status, err = exchange.RestoreExchangeDataCollections(ctx, creds, gc.Service, dest, dcs, deets) case selectors.ServiceOneDrive: - status, err = onedrive.RestoreCollections(ctx, gc.Service, dest, opts, dcs, deets) + status, err = onedrive.RestoreCollections(ctx, backupVersion, gc.Service, dest, opts, dcs, deets) case selectors.ServiceSharePoint: - status, err = sharepoint.RestoreCollections(ctx, gc.Service, dest, dcs, deets) + status, err = sharepoint.RestoreCollections(ctx, backupVersion, gc.Service, dest, dcs, deets) default: err = errors.Errorf("restore data from service %s not supported", selector.Service.String()) } diff --git a/src/internal/connector/graph_connector_helper_test.go b/src/internal/connector/graph_connector_helper_test.go index 8a0e22a26..539cbf501 100644 --- a/src/internal/connector/graph_connector_helper_test.go +++ b/src/internal/connector/graph_connector_helper_test.go @@ -172,6 +172,14 @@ type restoreBackupInfo struct { resource resource } +type restoreBackupInfoMultiVersion struct { + name string + service path.ServiceType + collectionsLatest []colInfo + collectionsPrevious []colInfo + resource resource +} + func attachmentEqual( expected models.Attachmentable, got models.Attachmentable, @@ -653,7 +661,7 @@ func compareOneDriveItem( name := item.UUID() expectedData := expected[item.UUID()] - if !assert.NotNil(t, expectedData, "unexpected file with name %s", item.UUID) { + if !assert.NotNil(t, expectedData, "unexpected file with name %s", item.UUID()) { return } @@ -988,6 +996,52 @@ func collectionsForInfo( return totalItems, kopiaEntries, collections, expectedData } +func collectionsForInfoVersion0( + t *testing.T, + service path.ServiceType, + tenant, user string, + dest control.RestoreDestination, + allInfo []colInfo, +) (int, int, []data.Collection, map[string]map[string][]byte) { + collections := make([]data.Collection, 0, len(allInfo)) + expectedData := make(map[string]map[string][]byte, len(allInfo)) + totalItems := 0 + kopiaEntries := 0 + + for _, info := range allInfo { + pth := mustToDataLayerPath( + t, + service, + tenant, + user, + info.category, + info.pathElements, + false, + ) + c := mockconnector.NewMockExchangeCollection(pth, len(info.items)) + baseDestPath := backupOutputPathFromRestore(t, dest, pth) + + baseExpected := expectedData[baseDestPath.String()] + if baseExpected == nil { + expectedData[baseDestPath.String()] = make(map[string][]byte, len(info.items)) + baseExpected = expectedData[baseDestPath.String()] + } + + for i := 0; i < len(info.items); i++ { + c.Names[i] = info.items[i].name + c.Data[i] = info.items[i].data + + baseExpected[info.items[i].lookupKey] = info.items[i].data + } + + collections = append(collections, c) + totalItems += len(info.items) + kopiaEntries += len(info.items) + } + + return totalItems, kopiaEntries, collections, expectedData +} + //nolint:deadcode func getSelectorWith( t *testing.T, diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 2c2280e37..1cdb0b59e 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -22,6 +22,7 @@ import ( "github.com/alcionai/corso/src/internal/data" "github.com/alcionai/corso/src/internal/tester" "github.com/alcionai/corso/src/pkg/account" + "github.com/alcionai/corso/src/pkg/backup" "github.com/alcionai/corso/src/pkg/control" "github.com/alcionai/corso/src/pkg/path" "github.com/alcionai/corso/src/pkg/selectors" @@ -231,7 +232,15 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreFailsBadService() { } ) - deets, err := suite.connector.RestoreDataCollections(ctx, acct, sel, dest, control.Options{}, nil) + deets, err := suite.connector.RestoreDataCollections( + ctx, + backup.Version, + acct, + sel, + dest, + control.Options{}, + nil, + ) assert.Error(t, err) assert.NotNil(t, deets) @@ -299,6 +308,7 @@ func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() { deets, err := suite.connector.RestoreDataCollections( ctx, + backup.Version, suite.acct, test.sel, dest, @@ -393,6 +403,7 @@ func runRestoreBackupTest( restoreSel := getSelectorWith(t, test.service, resourceOwners, true) deets, err := restoreGC.RestoreDataCollections( ctx, + backup.Version, acct, restoreSel, dest, @@ -458,6 +469,121 @@ func runRestoreBackupTest( "backup status.Successful; wanted %d items + %d skipped", totalItems, skipped) } +// runRestoreBackupTestVersion0 restores with data from an older +// version of the backup and check the restored data against the +// something that would be in the form of a newer backup. +func runRestoreBackupTestVersion0( + t *testing.T, + acct account.Account, + test restoreBackupInfoMultiVersion, + tenant string, + resourceOwners []string, + opts control.Options, +) { + var ( + collections []data.Collection + expectedData = map[string]map[string][]byte{} + totalItems = 0 + totalKopiaItems = 0 + // Get a dest per test so they're independent. + dest = tester.DefaultTestRestoreDestination() + ) + + ctx, flush := tester.NewContext() + defer flush() + + for _, owner := range resourceOwners { + _, _, ownerCollections, _ := collectionsForInfoVersion0( + t, + test.service, + tenant, + owner, + dest, + test.collectionsPrevious, + ) + + collections = append(collections, ownerCollections...) + } + + t.Logf( + "Restoring collections to %s for resourceOwners(s) %v\n", + dest.ContainerName, + resourceOwners, + ) + + start := time.Now() + + restoreGC := loadConnector(ctx, t, graph.HTTPClient(graph.NoTimeout()), test.resource) + restoreSel := getSelectorWith(t, test.service, resourceOwners, true) + deets, err := restoreGC.RestoreDataCollections( + ctx, + 0, // The OG version ;) + acct, + restoreSel, + dest, + opts, + collections, + ) + require.NoError(t, err) + assert.NotNil(t, deets) + + assert.NotNil(t, restoreGC.AwaitStatus()) + + runTime := time.Since(start) + + t.Logf("Restore complete in %v\n", runTime) + + // Run a backup and compare its output with what we put in. + for _, owner := range resourceOwners { + numItems, kopiaItems, _, userExpectedData := collectionsForInfo( + t, + test.service, + tenant, + owner, + dest, + test.collectionsLatest, + ) + + totalItems += numItems + totalKopiaItems += kopiaItems + + maps.Copy(expectedData, userExpectedData) + } + + cats := make(map[path.CategoryType]struct{}, len(test.collectionsLatest)) + for _, c := range test.collectionsLatest { + cats[c.category] = struct{}{} + } + + expectedDests := make([]destAndCats, 0, len(resourceOwners)) + for _, ro := range resourceOwners { + expectedDests = append(expectedDests, destAndCats{ + resourceOwner: ro, + dest: dest.ContainerName, + cats: cats, + }) + } + + backupGC := loadConnector(ctx, t, graph.HTTPClient(graph.NoTimeout()), test.resource) + backupSel := backupSelectorForExpected(t, test.service, expectedDests) + + start = time.Now() + dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) + require.NoError(t, err) + // No excludes yet because this isn't an incremental backup. + assert.Empty(t, excludes) + + t.Logf("Backup enumeration complete in %v\n", time.Since(start)) + + // Pull the data prior to waiting for the status as otherwise it will + // deadlock. + skipped := checkCollections(t, totalKopiaItems, expectedData, dcs, opts.RestorePermissions) + + status := backupGC.AwaitStatus() + assert.Equal(t, totalItems+skipped, status.ObjectCount, "status.ObjectCount") + assert.Equal(t, totalItems+skipped, status.Successful, "status.Successful") +} + func getTestMetaJSON(t *testing.T, user string, roles []string) []byte { id := base64.StdEncoding.EncodeToString([]byte(user + strings.Join(roles, "+"))) testMeta := onedrive.Metadata{Permissions: []onedrive.UserPermission{ @@ -906,6 +1032,255 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { } } +func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackupVersion0() { + ctx, flush := tester.NewContext() + defer flush() + + // Get the default drive ID for the test user. + driveID := mustGetDefaultDriveID( + suite.T(), + ctx, + suite.connector.Service, + suite.user, + ) + + table := []restoreBackupInfoMultiVersion{ + { + name: "OneDriveMultipleFoldersAndFiles", + service: path.OneDriveService, + resource: Users, + + collectionsPrevious: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt", + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt", + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt", + data: []byte(strings.Repeat("b", 65)), + lookupKey: "test-file.txt", + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt", + data: []byte(strings.Repeat("c", 129)), + lookupKey: "test-file.txt", + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + "b", + "folder-a", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt", + data: []byte(strings.Repeat("d", 257)), + lookupKey: "test-file.txt", + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt", + data: []byte(strings.Repeat("e", 257)), + lookupKey: "test-file.txt", + }, + }, + }, + }, + + collectionsLatest: []colInfo{ + { + pathElements: []string{ + "drives", + driveID, + "root:", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("a", 33)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "folder-a" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "folder-a" + onedrive.DirMetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("b", 65)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "b" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "b" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("c", 129)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + { + name: "folder-a" + onedrive.DirMetaFileSuffix, + data: []byte("{}"), + lookupKey: "folder-a" + onedrive.DirMetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "folder-a", + "b", + "folder-a", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("d", 257)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + { + pathElements: []string{ + "drives", + driveID, + "root:", + "b", + }, + category: path.FilesCategory, + items: []itemInfo{ + { + name: "test-file.txt" + onedrive.DataFileSuffix, + data: []byte(strings.Repeat("e", 257)), + lookupKey: "test-file.txt" + onedrive.DataFileSuffix, + }, + { + name: "test-file.txt" + onedrive.MetaFileSuffix, + data: []byte("{}"), + lookupKey: "test-file.txt" + onedrive.MetaFileSuffix, + }, + }, + }, + }, + }, + } + + for _, test := range table { + suite.T().Run(test.name, func(t *testing.T) { + runRestoreBackupTestVersion0( + t, + suite.acct, + test, + suite.connector.tenant, + []string{suite.user}, + control.Options{RestorePermissions: true}, + ) + }) + } +} + func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames() { table := []restoreBackupInfo{ { @@ -1012,6 +1387,7 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames restoreGC := loadConnector(ctx, t, graph.HTTPClient(graph.NoTimeout()), test.resource) deets, err := restoreGC.RestoreDataCollections( ctx, + backup.Version, suite.acct, restoreSel, dest, diff --git a/src/internal/connector/onedrive/restore.go b/src/internal/connector/onedrive/restore.go index af591cd86..0014457c4 100644 --- a/src/internal/connector/onedrive/restore.go +++ b/src/internal/connector/onedrive/restore.go @@ -29,6 +29,11 @@ const ( // Microsoft recommends 5-10MB buffers // https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#best-practices copyBufferSize = 5 * 1024 * 1024 + + // versionWithDataAndMetaFiles is the corso backup format version + // in which we split from storing just the data to storing both + // the data and metadata in two files. + versionWithDataAndMetaFiles = 1 ) func getParentPermissions( @@ -55,6 +60,7 @@ func getParentPermissions( // RestoreCollections will restore the specified data collections into OneDrive func RestoreCollections( ctx context.Context, + backupVersion int, service graph.Servicer, dest control.RestoreDestination, opts control.Options, @@ -101,6 +107,7 @@ func RestoreCollections( metrics, folderPerms, permissionIDMappings, canceled = RestoreCollection( ctx, + backupVersion, service, dc, parentPerms, @@ -139,6 +146,7 @@ func RestoreCollections( // - the context cancellation state (true if the context is canceled) func RestoreCollection( ctx context.Context, + backupVersion int, service graph.Servicer, dc data.Collection, parentPerms []UserPermission, @@ -211,7 +219,7 @@ func RestoreCollection( continue } - if source == OneDriveSource { + if source == OneDriveSource && backupVersion >= versionWithDataAndMetaFiles { name := itemData.UUID() if strings.HasSuffix(name, DataFileSuffix) { metrics.Objects++ diff --git a/src/internal/connector/sharepoint/restore.go b/src/internal/connector/sharepoint/restore.go index 4784ed209..3cf35d287 100644 --- a/src/internal/connector/sharepoint/restore.go +++ b/src/internal/connector/sharepoint/restore.go @@ -36,6 +36,7 @@ import ( // RestoreCollections will restore the specified data collections into OneDrive func RestoreCollections( ctx context.Context, + backupVersion int, service graph.Servicer, dest control.RestoreDestination, dcs []data.Collection, @@ -61,6 +62,7 @@ func RestoreCollections( case path.LibrariesCategory: metrics, _, _, canceled = onedrive.RestoreCollection( ctx, + backupVersion, service, dc, []onedrive.UserPermission{}, // Currently permission data is not stored for sharepoint diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index a57a9d2be..5e9af1c46 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -339,7 +339,15 @@ func generateContainerOfItems( dest, collections) - deets, err := gc.RestoreDataCollections(ctx, acct, sel, dest, control.Options{RestorePermissions: true}, dataColls) + deets, err := gc.RestoreDataCollections( + ctx, + backup.Version, + acct, + sel, + dest, + control.Options{RestorePermissions: true}, + dataColls, + ) require.NoError(t, err) return deets diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index 206eb8026..cd52e5be3 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -219,6 +219,7 @@ func (op *RestoreOperation) do(ctx context.Context) (restoreDetails *details.Det restoreDetails, err = gc.RestoreDataCollections( ctx, + bup.Version, op.account, op.Selectors, op.Destination, diff --git a/src/pkg/backup/backup.go b/src/pkg/backup/backup.go index d0d9ddffd..4422b3a47 100644 --- a/src/pkg/backup/backup.go +++ b/src/pkg/backup/backup.go @@ -14,6 +14,8 @@ import ( "github.com/alcionai/corso/src/pkg/selectors" ) +const Version = 1 + // Backup represents the result of a backup operation type Backup struct { model.BaseModel @@ -32,6 +34,9 @@ type Backup struct { // Selector used in this operation Selector selectors.Selector `json:"selectors"` + // Version represents the version of the backup format + Version int `json:"version"` + // Errors contains all errors aggregated during a backup operation. Errors fault.ErrorsData `json:"errors"` @@ -67,6 +72,7 @@ func New( Errors: errs.Data(), ReadWrites: rw, StartAndEndTime: se, + Version: Version, } } From 76c2ac628b7b34f2b532b36ba7821f9f63cbc8b0 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 3 Feb 2023 10:38:28 +0530 Subject: [PATCH 43/46] Fix some edge cases around OneDrive permissions backup (#2370) ## Description This fixes tying to parse link shares as permissions and some error/retry handling. ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [x] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * # ## Test Plan - [x] :muscle: Manual - [x] :zap: Unit test - [ ] :green_heart: E2E --- CHANGELOG.md | 1 + src/internal/connector/onedrive/collection.go | 6 +- src/internal/connector/onedrive/item.go | 18 ++++- src/internal/connector/onedrive/item_test.go | 70 +++++++++++++++++++ 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d3845da..53ec01a36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - When the same user has permissions to a file and the containing folder, we only restore folder level permissions for the user and no separate file only permission is restored. +- Link shares are not restored ## [v0.2.0] (alpha) - 2023-1-29 diff --git a/src/internal/connector/onedrive/collection.go b/src/internal/connector/onedrive/collection.go index 3b8ff5cbb..2de05c073 100644 --- a/src/internal/connector/onedrive/collection.go +++ b/src/internal/connector/onedrive/collection.go @@ -292,7 +292,9 @@ func (oc *Collection) populateItems(ctx context.Context) { itemMeta, itemMetaSize, err = oc.itemMetaReader(ctx, oc.service, oc.driveID, item) // retry on Timeout type errors, break otherwise. - if err == nil || !graph.IsErrTimeout(err) { + if err == nil || + !graph.IsErrTimeout(err) || + !graph.IsInternalServerError(err) { break } @@ -302,7 +304,7 @@ func (oc *Collection) populateItems(ctx context.Context) { } if err != nil { - errUpdater(*item.GetId(), err) + errUpdater(*item.GetId(), errors.Wrap(err, "failed to get item permissions")) return } } diff --git a/src/internal/connector/onedrive/item.go b/src/internal/connector/onedrive/item.go index 1526f1401..c527ce09b 100644 --- a/src/internal/connector/onedrive/item.go +++ b/src/internal/connector/onedrive/item.go @@ -191,12 +191,24 @@ func oneDriveItemMetaInfo( perm, err := service.Client().DrivesById(driveID).ItemsById(*itemID).Permissions().Get(ctx, nil) if err != nil { - return Metadata{}, errors.Wrapf(err, "failed to get item permissions %s", *itemID) + return Metadata{}, err } + uperms := filterUserPermissions(perm.GetValue()) + + return Metadata{Permissions: uperms}, nil +} + +func filterUserPermissions(perms []models.Permissionable) []UserPermission { up := []UserPermission{} - for _, p := range perm.GetValue() { + for _, p := range perms { + if p.GetGrantedToV2() == nil { + // For link shares, we get permissions without a user + // specified + continue + } + roles := []string{} for _, r := range p.GetRoles() { @@ -218,7 +230,7 @@ func oneDriveItemMetaInfo( }) } - return Metadata{Permissions: up}, nil + return up } // sharePointItemInfo will populate a details.SharePointInfo struct diff --git a/src/internal/connector/onedrive/item_test.go b/src/internal/connector/onedrive/item_test.go index b0e42943a..a2e008ec5 100644 --- a/src/internal/connector/onedrive/item_test.go +++ b/src/internal/connector/onedrive/item_test.go @@ -8,6 +8,7 @@ import ( msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" "github.com/microsoftgraph/msgraph-sdk-go/models" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" @@ -257,3 +258,72 @@ func (suite *ItemIntegrationSuite) TestDriveGetFolder() { }) } } + +func getPermsUperms(permID, userID string, scopes []string) (models.Permissionable, UserPermission) { + identity := models.NewIdentity() + identity.SetAdditionalData(map[string]any{"email": &userID}) + + sharepointIdentity := models.NewSharePointIdentitySet() + sharepointIdentity.SetUser(identity) + + perm := models.NewPermission() + perm.SetId(&permID) + perm.SetRoles([]string{"read"}) + perm.SetGrantedToV2(sharepointIdentity) + + uperm := UserPermission{ + ID: permID, + Roles: []string{"read"}, + Email: userID, + } + + return perm, uperm +} + +func TestOneDrivePermissionsFilter(t *testing.T) { + permID := "fakePermId" + userID := "fakeuser@provider.com" + userID2 := "fakeuser2@provider.com" + + readPerm, readUperm := getPermsUperms(permID, userID, []string{"read"}) + readWritePerm, readWriteUperm := getPermsUperms(permID, userID2, []string{"read", "write"}) + + noPerm, _ := getPermsUperms(permID, userID, []string{"read"}) + noPerm.SetGrantedToV2(nil) // eg: link shares + + cases := []struct { + name string + graphPermissions []models.Permissionable + parsedPermissions []UserPermission + }{ + { + name: "no perms", + graphPermissions: []models.Permissionable{}, + parsedPermissions: []UserPermission{}, + }, + { + name: "no user bound to perms", + graphPermissions: []models.Permissionable{noPerm}, + parsedPermissions: []UserPermission{}, + }, + { + name: "user with read permissions", + graphPermissions: []models.Permissionable{readPerm}, + parsedPermissions: []UserPermission{readUperm}, + }, + { + name: "user with read and write permissions", + graphPermissions: []models.Permissionable{readWritePerm}, + parsedPermissions: []UserPermission{readWriteUperm}, + }, + { + name: "multiple users with separate permissions", + graphPermissions: []models.Permissionable{readPerm, readWritePerm}, + parsedPermissions: []UserPermission{readUperm, readWriteUperm}, + }, + } + for _, tc := range cases { + actual := filterUserPermissions(tc.graphPermissions) + assert.ElementsMatch(t, tc.parsedPermissions, actual) + } +} From 0be38909dd2fc4eba13f1aba08ba0298dfb953a7 Mon Sep 17 00:00:00 2001 From: Keepers Date: Thu, 2 Feb 2023 23:18:41 -0700 Subject: [PATCH 44/46] handle error from bu.backupCollections (#2386) ## Does this PR need a docs update or release note? - [x] :no_entry: No ## Type of change - [x] :bug: Bugfix ## Test Plan - [x] :green_heart: E2E --- src/internal/operations/backup.go | 36 +++++++++++++++++------------- src/internal/operations/restore.go | 6 +++++ 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/src/internal/operations/backup.go b/src/internal/operations/backup.go index e0df72d55..e6b8c767a 100644 --- a/src/internal/operations/backup.go +++ b/src/internal/operations/backup.go @@ -167,6 +167,12 @@ func (op *BackupOperation) do(ctx context.Context) (err error) { // persist operation results to the model store on exit defer func() { + // panic recovery here prevents additional errors in op.persistResults() + if r := recover(); r != nil { + err = clues.Wrap(r.(error), "panic recovery").WithClues(ctx).With("stacktrace", debug.Stack()) + return + } + err = op.persistResults(startTime, &opStats) if err != nil { op.Errors.Fail(errors.Wrap(err, "persisting backup results")) @@ -445,24 +451,22 @@ func consumeBackupDataCollections( cs, nil, tags, - isIncremental, - ) + isIncremental) + if err != nil { + if kopiaStats == nil { + return nil, nil, nil, err + } + + return nil, nil, nil, errors.Wrapf( + err, + "kopia snapshot failed with %v catastrophic errors and %v ignored errors", + kopiaStats.ErrorCount, kopiaStats.IgnoredErrorCount) + } if kopiaStats.ErrorCount > 0 || kopiaStats.IgnoredErrorCount > 0 { - if err != nil { - err = errors.Wrapf( - err, - "kopia snapshot failed with %v catastrophic errors and %v ignored errors", - kopiaStats.ErrorCount, - kopiaStats.IgnoredErrorCount, - ) - } else { - err = errors.Errorf( - "kopia snapshot failed with %v catastrophic errors and %v ignored errors", - kopiaStats.ErrorCount, - kopiaStats.IgnoredErrorCount, - ) - } + err = errors.Errorf( + "kopia snapshot failed with %v catastrophic errors and %v ignored errors", + kopiaStats.ErrorCount, kopiaStats.IgnoredErrorCount) } return kopiaStats, deets, itemsSourcedFromBase, err diff --git a/src/internal/operations/restore.go b/src/internal/operations/restore.go index cd52e5be3..f90e3c3c8 100644 --- a/src/internal/operations/restore.go +++ b/src/internal/operations/restore.go @@ -151,6 +151,12 @@ func (op *RestoreOperation) do(ctx context.Context) (restoreDetails *details.Det ) defer func() { + // panic recovery here prevents additional errors in op.persistResults() + if r := recover(); r != nil { + err = clues.Wrap(r.(error), "panic recovery").WithClues(ctx).With("stacktrace", debug.Stack()) + return + } + err = op.persistResults(ctx, startTime, &opStats) if err != nil { return From 8d01d1b397f19cb965015ccb820be7a6a6899fb1 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 3 Feb 2023 12:39:05 +0530 Subject: [PATCH 45/46] Disable OneDrive permissions backup by default (#2374) ## Description Since the backup permissions increases the backup time by a lot(mostly from higher number of requests and increased throttling), it was decided to disable it by default. It is still enabled in tests so as to make sure the code does not have regressions. ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [x] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [ ] :broom: Tech Debt/Cleanup ## Issue(s) * # ## Test Plan - [ ] :muscle: Manual - [ ] :zap: Unit test - [x] :green_heart: E2E --- src/cli/backup/onedrive.go | 2 +- src/cli/backup/onedrive_integration_test.go | 16 +++- src/cli/options/options.go | 22 +++--- .../connector/graph_connector_test.go | 74 +++++++++++++++---- src/internal/connector/onedrive/collection.go | 7 +- .../connector/onedrive/collection_test.go | 6 +- .../connector/onedrive/collections_test.go | 4 +- src/internal/connector/onedrive/drive_test.go | 2 +- .../operations/backup_integration_test.go | 2 +- src/pkg/control/options.go | 4 +- 10 files changed, 101 insertions(+), 38 deletions(-) diff --git a/src/cli/backup/onedrive.go b/src/cli/backup/onedrive.go index f74f98916..9dfb20b79 100644 --- a/src/cli/backup/onedrive.go +++ b/src/cli/backup/onedrive.go @@ -79,7 +79,7 @@ func addOneDriveCommands(cmd *cobra.Command) *cobra.Command { switch cmd.Use { case createCommand: c, fs = utils.AddCommand(cmd, oneDriveCreateCmd()) - options.AddFeatureToggle(cmd, options.DisablePermissionsBackup()) + options.AddFeatureToggle(cmd, options.EnablePermissionsBackup()) c.Use = c.Use + " " + oneDriveServiceCommandCreateUseSuffix c.Example = oneDriveServiceCommandCreateExamples diff --git a/src/cli/backup/onedrive_integration_test.go b/src/cli/backup/onedrive_integration_test.go index e24cba34f..05231fd11 100644 --- a/src/cli/backup/onedrive_integration_test.go +++ b/src/cli/backup/onedrive_integration_test.go @@ -72,7 +72,13 @@ func (suite *NoBackupOneDriveIntegrationSuite) SetupSuite() { suite.m365UserID = tester.M365UserID(t) // init the repo first - suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{}) + suite.repo, err = repository.Initialize( + ctx, + suite.acct, + suite.st, + control.Options{ + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }) require.NoError(t, err) } @@ -152,7 +158,13 @@ func (suite *BackupDeleteOneDriveIntegrationSuite) SetupSuite() { defer flush() // init the repo first - suite.repo, err = repository.Initialize(ctx, suite.acct, suite.st, control.Options{}) + suite.repo, err = repository.Initialize( + ctx, + suite.acct, + suite.st, + control.Options{ + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }) require.NoError(t, err) m365UserID := tester.M365UserID(t) diff --git a/src/cli/options/options.go b/src/cli/options/options.go index 32defc5bb..2b423836c 100644 --- a/src/cli/options/options.go +++ b/src/cli/options/options.go @@ -15,7 +15,7 @@ func Control() control.Options { opt.DisableMetrics = noStats opt.RestorePermissions = restorePermissions opt.ToggleFeatures.DisableIncrementals = disableIncrementals - opt.ToggleFeatures.DisablePermissionsBackup = disablePermissionsBackup + opt.ToggleFeatures.EnablePermissionsBackup = enablePermissionsBackup return opt } @@ -48,6 +48,8 @@ func AddGlobalOperationFlags(cmd *cobra.Command) { func AddRestorePermissionsFlag(cmd *cobra.Command) { fs := cmd.Flags() fs.BoolVar(&restorePermissions, "restore-permissions", false, "Restore permissions for files and folders") + // TODO: reveal this flag once backing up permissions becomes default + cobra.CheckErr(fs.MarkHidden("restore-permissions")) } // --------------------------------------------------------------------------- @@ -55,8 +57,8 @@ func AddRestorePermissionsFlag(cmd *cobra.Command) { // --------------------------------------------------------------------------- var ( - disableIncrementals bool - disablePermissionsBackup bool + disableIncrementals bool + enablePermissionsBackup bool ) type exposeFeatureFlag func(*pflag.FlagSet) @@ -83,15 +85,15 @@ func DisableIncrementals() func(*pflag.FlagSet) { } } -// Adds the hidden '--disable-permissions-backup' cli flag which, when -// set, disables backing up permissions. -func DisablePermissionsBackup() func(*pflag.FlagSet) { +// Adds the hidden '--enable-permissions-backup' cli flag which, when +// set, enables backing up permissions. +func EnablePermissionsBackup() func(*pflag.FlagSet) { return func(fs *pflag.FlagSet) { fs.BoolVar( - &disablePermissionsBackup, - "disable-permissions-backup", + &enablePermissionsBackup, + "enable-permissions-backup", false, - "Disable backing up item permissions for OneDrive") - cobra.CheckErr(fs.MarkHidden("disable-permissions-backup")) + "Enable backing up item permissions for OneDrive") + cobra.CheckErr(fs.MarkHidden("enable-permissions-backup")) } } diff --git a/src/internal/connector/graph_connector_test.go b/src/internal/connector/graph_connector_test.go index 1cdb0b59e..b3b55a15e 100644 --- a/src/internal/connector/graph_connector_test.go +++ b/src/internal/connector/graph_connector_test.go @@ -238,7 +238,10 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreFailsBadService() { acct, sel, dest, - control.Options{}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, nil, ) assert.Error(t, err) @@ -312,7 +315,10 @@ func (suite *GraphConnectorIntegrationSuite) TestEmptyCollections() { suite.acct, test.sel, dest, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, test.col, ) require.NoError(t, err) @@ -448,7 +454,15 @@ func runRestoreBackupTest( t.Logf("Selective backup of %s\n", backupSel) start = time.Now() - dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) + dcs, excludes, err := backupGC.DataCollections( + ctx, + backupSel, + nil, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, + ) require.NoError(t, err) // No excludes yet because this isn't an incremental backup. assert.Empty(t, excludes) @@ -568,7 +582,15 @@ func runRestoreBackupTestVersion0( backupSel := backupSelectorForExpected(t, test.service, expectedDests) start = time.Now() - dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) + dcs, excludes, err := backupGC.DataCollections( + ctx, + backupSel, + nil, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, + ) require.NoError(t, err) // No excludes yet because this isn't an incremental backup. assert.Empty(t, excludes) @@ -1026,7 +1048,10 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup() { test, suite.connector.tenant, []string{suite.user}, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, ) }) } @@ -1275,7 +1300,10 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackupVersion0() { test, suite.connector.tenant, []string{suite.user}, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, ) }) } @@ -1391,7 +1419,10 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames suite.acct, restoreSel, dest, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, collections, ) require.NoError(t, err) @@ -1414,7 +1445,15 @@ func (suite *GraphConnectorIntegrationSuite) TestMultiFolderBackupDifferentNames backupSel := backupSelectorForExpected(t, test.service, expectedDests) t.Log("Selective backup of", backupSel) - dcs, excludes, err := backupGC.DataCollections(ctx, backupSel, nil, control.Options{RestorePermissions: true}) + dcs, excludes, err := backupGC.DataCollections( + ctx, + backupSel, + nil, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, + ) require.NoError(t, err) // No excludes yet because this isn't an incremental backup. assert.Empty(t, excludes) @@ -1446,7 +1485,7 @@ func (suite *GraphConnectorIntegrationSuite) TestPermissionsRestoreAndBackup() { table := []restoreBackupInfo{ { - name: "FilePermissionsResote", + name: "FilePermissionsRestore", service: path.OneDriveService, resource: Users, collections: []colInfo{ @@ -1677,7 +1716,10 @@ func (suite *GraphConnectorIntegrationSuite) TestPermissionsRestoreAndBackup() { test, suite.connector.tenant, []string{suite.user}, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, ) }) } @@ -1697,7 +1739,7 @@ func (suite *GraphConnectorIntegrationSuite) TestPermissionsBackupAndNoRestore() table := []restoreBackupInfo{ { - name: "FilePermissionsResote", + name: "FilePermissionsRestore", service: path.OneDriveService, resource: Users, collections: []colInfo{ @@ -1733,7 +1775,10 @@ func (suite *GraphConnectorIntegrationSuite) TestPermissionsBackupAndNoRestore() test, suite.connector.tenant, []string{suite.user}, - control.Options{RestorePermissions: false}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, ) }) } @@ -1769,6 +1814,9 @@ func (suite *GraphConnectorIntegrationSuite) TestRestoreAndBackup_largeMailAttac test, suite.connector.tenant, []string{suite.user}, - control.Options{RestorePermissions: true}, + control.Options{ + RestorePermissions: true, + ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}, + }, ) } diff --git a/src/internal/connector/onedrive/collection.go b/src/internal/connector/onedrive/collection.go index 2de05c073..343a8911e 100644 --- a/src/internal/connector/onedrive/collection.go +++ b/src/internal/connector/onedrive/collection.go @@ -279,10 +279,11 @@ func (oc *Collection) populateItems(ctx context.Context) { if oc.source == OneDriveSource { // Fetch metadata for the file for i := 1; i <= maxRetries; i++ { - if oc.ctrl.ToggleFeatures.DisablePermissionsBackup { + if !oc.ctrl.ToggleFeatures.EnablePermissionsBackup { // We are still writing the metadata file but with - // empty permissions as we are not sure how the - // restore will be called. + // empty permissions as we don't have a way to + // signify that the permissions was explicitly + // not added. itemMeta = io.NopCloser(strings.NewReader("{}")) itemMetaSize = 2 diff --git a/src/internal/connector/onedrive/collection_test.go b/src/internal/connector/onedrive/collection_test.go index b8e5fe446..734009d72 100644 --- a/src/internal/connector/onedrive/collection_test.go +++ b/src/internal/connector/onedrive/collection_test.go @@ -168,7 +168,7 @@ func (suite *CollectionUnitTestSuite) TestCollection() { suite, suite.testStatusUpdater(&wg, &collStatus), test.source, - control.Options{}) + control.Options{ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}}) require.NotNil(t, coll) assert.Equal(t, folderPath, coll.FullPath()) @@ -301,7 +301,7 @@ func (suite *CollectionUnitTestSuite) TestCollectionReadError() { suite, suite.testStatusUpdater(&wg, &collStatus), test.source, - control.Options{}) + control.Options{ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}}) mockItem := models.NewDriveItem() mockItem.SetId(&testItemID) @@ -372,7 +372,7 @@ func (suite *CollectionUnitTestSuite) TestCollectionDisablePermissionsBackup() { suite, suite.testStatusUpdater(&wg, &collStatus), test.source, - control.Options{ToggleFeatures: control.Toggles{DisablePermissionsBackup: true}}) + control.Options{ToggleFeatures: control.Toggles{}}) now := time.Now() mockItem := models.NewDriveItem() diff --git a/src/internal/connector/onedrive/collections_test.go b/src/internal/connector/onedrive/collections_test.go index 3316a10c5..f784bad62 100644 --- a/src/internal/connector/onedrive/collections_test.go +++ b/src/internal/connector/onedrive/collections_test.go @@ -635,7 +635,7 @@ func (suite *OneDriveCollectionsSuite) TestUpdateCollections() { testFolderMatcher{tt.scope}, &MockGraphService{}, nil, - control.Options{}) + control.Options{ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}}) err := c.UpdateCollections( ctx, @@ -1380,7 +1380,7 @@ func (suite *OneDriveCollectionsSuite) TestGet() { testFolderMatcher{anyFolder}, &MockGraphService{}, func(*support.ConnectorOperationStatus) {}, - control.Options{}, + control.Options{ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}}, ) c.drivePagerFunc = drivePagerFunc c.itemPagerFunc = itemPagerFunc diff --git a/src/internal/connector/onedrive/drive_test.go b/src/internal/connector/onedrive/drive_test.go index 0ba3ec1c2..a67c89ab1 100644 --- a/src/internal/connector/onedrive/drive_test.go +++ b/src/internal/connector/onedrive/drive_test.go @@ -462,7 +462,7 @@ func (suite *OneDriveSuite) TestOneDriveNewCollections() { testFolderMatcher{scope}, service, service.updateStatus, - control.Options{}, + control.Options{ToggleFeatures: control.Toggles{EnablePermissionsBackup: true}}, ).Get(ctx, nil) assert.NoError(t, err) // Don't expect excludes as this isn't an incremental backup. diff --git a/src/internal/operations/backup_integration_test.go b/src/internal/operations/backup_integration_test.go index 5e9af1c46..b3ea617d9 100644 --- a/src/internal/operations/backup_integration_test.go +++ b/src/internal/operations/backup_integration_test.go @@ -1081,7 +1081,7 @@ func (suite *BackupOpIntegrationSuite) TestBackup_Run_oneDrive() { sel.Include(sel.AllData()) - bo, _, _, _, closer := prepNewTestBackupOp(t, ctx, mb, sel.Selector, control.Toggles{}) + bo, _, _, _, closer := prepNewTestBackupOp(t, ctx, mb, sel.Selector, control.Toggles{EnablePermissionsBackup: true}) defer closer() runAndCheckBackup(t, ctx, &bo, mb) diff --git a/src/pkg/control/options.go b/src/pkg/control/options.go index 6f53839ca..7348e8fa6 100644 --- a/src/pkg/control/options.go +++ b/src/pkg/control/options.go @@ -76,8 +76,8 @@ type Toggles struct { // forcing a new, complete backup of all data regardless of prior state. DisableIncrementals bool `json:"exchangeIncrementals,omitempty"` - // DisablePermissionsBackup is used to disable backups of item + // EnablePermissionsBackup is used to enable backups of item // permissions. Permission metadata increases graph api call count, // so disabling their retrieval when not needed is advised. - DisablePermissionsBackup bool `json:"disablePermissionsBackup,omitempty"` + EnablePermissionsBackup bool `json:"enablePermissionsBackup,omitempty"` } From 272a8b30fe05afbb62c20b651a0d264f32ed52b4 Mon Sep 17 00:00:00 2001 From: Abin Simon Date: Fri, 3 Feb 2023 13:37:03 +0530 Subject: [PATCH 46/46] Run gci on beta package (#2368) ## Description Followup to https://github.com/alcionai/corso/pull/2340 . Runs `gci write --skip-generated -s 'standard,default,prefix(github.com/alcionai/corso)'` to properly order headers. This is generated files as mentioned in the previous PR, but I thought we might as well order the headers as well if we are formatting it. Let me know if leaving this out was intentional though. ## Does this PR need a docs update or release note? - [ ] :white_check_mark: Yes, it's included - [ ] :clock1: Yes, but in a later PR - [x] :no_entry: No ## Type of change - [ ] :sunflower: Feature - [ ] :bug: Bugfix - [ ] :world_map: Documentation - [ ] :robot: Test - [ ] :computer: CI/Deployment - [x] :broom: Tech Debt/Cleanup --- src/internal/connector/discovery/api/beta_service.go | 3 ++- src/internal/connector/exchange/api/contacts.go | 2 +- src/internal/connector/exchange/api/events.go | 2 +- src/internal/connector/exchange/api/mail.go | 2 +- src/internal/connector/graph/betasdk/beta_client.go | 3 ++- .../graph/betasdk/models/site_page_collection_response.go | 1 - ...web_parts_item_get_position_of_web_part_request_builder.go | 3 ++- .../sites/item_pages_item_web_parts_request_builder.go | 3 ++- ...item_pages_item_web_parts_web_part_item_request_builder.go | 4 ++-- .../graph/betasdk/sites/item_pages_request_builder.go | 3 ++- src/pkg/logger/logger.go | 3 ++- 11 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/internal/connector/discovery/api/beta_service.go b/src/internal/connector/discovery/api/beta_service.go index df2b1533b..5ff65ac77 100644 --- a/src/internal/connector/discovery/api/beta_service.go +++ b/src/internal/connector/discovery/api/beta_service.go @@ -1,10 +1,11 @@ package api import ( - "github.com/alcionai/corso/src/internal/connector/graph/betasdk" absser "github.com/microsoft/kiota-abstractions-go/serialization" msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" "github.com/pkg/errors" + + "github.com/alcionai/corso/src/internal/connector/graph/betasdk" ) // Service wraps BetaClient's functionality. diff --git a/src/internal/connector/exchange/api/contacts.go b/src/internal/connector/exchange/api/contacts.go index 185bf6e22..458f364d1 100644 --- a/src/internal/connector/exchange/api/contacts.go +++ b/src/internal/connector/exchange/api/contacts.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/alcionai/clues" "github.com/hashicorp/go-multierror" "github.com/microsoft/kiota-abstractions-go/serialization" kioser "github.com/microsoft/kiota-serialization-json-go" @@ -12,7 +13,6 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" - "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index 810c1a2ff..70a1a45e9 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/alcionai/clues" "github.com/hashicorp/go-multierror" "github.com/microsoft/kiota-abstractions-go/serialization" kioser "github.com/microsoft/kiota-serialization-json-go" @@ -12,7 +13,6 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" - "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/common" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index 6a863322e..6acc05162 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -5,6 +5,7 @@ import ( "fmt" "time" + "github.com/alcionai/clues" "github.com/hashicorp/go-multierror" "github.com/microsoft/kiota-abstractions-go/serialization" kioser "github.com/microsoft/kiota-serialization-json-go" @@ -12,7 +13,6 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" - "github.com/alcionai/clues" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/graph/api" "github.com/alcionai/corso/src/internal/connector/support" diff --git a/src/internal/connector/graph/betasdk/beta_client.go b/src/internal/connector/graph/betasdk/beta_client.go index ef77b8169..f79ab8974 100644 --- a/src/internal/connector/graph/betasdk/beta_client.go +++ b/src/internal/connector/graph/betasdk/beta_client.go @@ -1,13 +1,14 @@ package betasdk import ( - i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/sites" absser "github.com/microsoft/kiota-abstractions-go" kioser "github.com/microsoft/kiota-abstractions-go/serialization" kform "github.com/microsoft/kiota-serialization-form-go" kw "github.com/microsoft/kiota-serialization-json-go" ktext "github.com/microsoft/kiota-serialization-text-go" msgraphsdk "github.com/microsoftgraph/msgraph-sdk-go" + + i1a3c1a5501c5e41b7fd169f2d4c768dce9b096ac28fb5431bf02afcc57295411 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/sites" ) // BetaClient the main entry point of the SDK, exposes the configuration and the fluent API. diff --git a/src/internal/connector/graph/betasdk/models/site_page_collection_response.go b/src/internal/connector/graph/betasdk/models/site_page_collection_response.go index f66cdafdf..bbd79c3a4 100644 --- a/src/internal/connector/graph/betasdk/models/site_page_collection_response.go +++ b/src/internal/connector/graph/betasdk/models/site_page_collection_response.go @@ -2,7 +2,6 @@ package models import ( i878a80d2330e89d26896388a3f487eef27b0a0e6c010c493bf80be1452208f91 "github.com/microsoft/kiota-abstractions-go/serialization" - msmodel "github.com/microsoftgraph/msgraph-sdk-go/models" ) diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go index 4bb325673..9db79ace5 100644 --- a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_item_get_position_of_web_part_request_builder.go @@ -3,9 +3,10 @@ package sites import ( "context" - ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" ) // ItemPagesItemWebPartsItemGetPositionOfWebPartRequestBuilder provides operations to call the getPositionOfWebPart method. diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go index 0e349df74..e2e32c640 100644 --- a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_request_builder.go @@ -3,9 +3,10 @@ package sites import ( "context" - ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" ) // ItemPagesItemWebPartsRequestBuilder provides operations to manage the webParts property of the microsoft.graph.sitePage entity. diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go index 25dba98cf..1c16fc8df 100644 --- a/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go +++ b/src/internal/connector/graph/betasdk/sites/item_pages_item_web_parts_web_part_item_request_builder.go @@ -3,10 +3,10 @@ package sites import ( "context" - ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" - i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" ) // ItemPagesItemWebPartsWebPartItemRequestBuilder provides operations to manage the webParts property of the microsoft.graph.sitePage entity. diff --git a/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go b/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go index 43f503439..6c82f58df 100644 --- a/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go +++ b/src/internal/connector/graph/betasdk/sites/item_pages_request_builder.go @@ -3,9 +3,10 @@ package sites import ( "context" - ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" i2ae4187f7daee263371cb1c977df639813ab50ffa529013b7437480d1ec0158f "github.com/microsoft/kiota-abstractions-go" i7ad325c11fbf3db4d761c429267362d8b24daa1eda0081f914ebc3cdc85181a0 "github.com/microsoftgraph/msgraph-sdk-go/models/odataerrors" + + ifda19816f54f079134d70c11e75d6b26799300cf72079e282f1d3bb9a6750354 "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" ) // ItemPagesRequestBuilder provides operations to manage the pages property of the microsoft.graph.site entity. diff --git a/src/pkg/logger/logger.go b/src/pkg/logger/logger.go index abbce9119..a6b5aa4dd 100644 --- a/src/pkg/logger/logger.go +++ b/src/pkg/logger/logger.go @@ -7,11 +7,12 @@ import ( "time" "github.com/alcionai/clues" - "github.com/alcionai/corso/src/cli/print" "github.com/spf13/cobra" "github.com/spf13/pflag" "go.uber.org/zap" "go.uber.org/zap/zapcore" + + "github.com/alcionai/corso/src/cli/print" ) // Default location for writing logs, initialized in platform specific files

id~6Km9tl_Uf@_XGg7y+z_XA6 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 aQOXTUfP2T_)`(-MV`oJk44B zN-BSw>s^|q0mkU}Ubw@6fB4>mQ@n7zV5b0n1aG7~AQWRxS-cIg4$B9x}8m-M>%IFgD?mV9(d^lmdf zTo|IXbR$M+u_gD10XIFNSMvJ{;5);f>E{<$W3<>T{4q3*V}3<(PX;W^LF`_?MA$Gr z!X&$)`g$|DnIcwL&<7FQ&}*n#3kv;%EZ9@*GW z+c{npev~Ixw@i*oiarB#C~+7hdKBq&u#ePRi;HY!QCD)S9T_3tGF?%NJ_r*!m0D*z z(;;93Gy#A2yD7Qam2Ob>)AFh2;LGvWb2(|2v6IzoA0_r;27g!=@V)3e>Q%knCYEOs zXGmEz+zO;*vc}rMytanyIel#9@|LCY_IUd(IcHLrKrblYRSx`wxqZ}EX?aX6lj|+y zK`iy{Mr)%`yI7UNHeX=hpKkvFQ}m^k8k+BB?c&X&s@4puI%#sXw(hK<=` zPo@I!Z&f(dKvX8|tPuUViaGAMw^yZ8)aauD1`9scTosO#4RZl8&Gp;sY#C1T6Gle%dDTDg=UANA+O`o2zTQxipe7Pkj z_*^9Zqklmld{(>J%pX55bejJ#s3dYso%XCx!-Mw!&t2uJ7}L}m4(bdw#75Qoi6!ZV z48KNkU?6yWwx-69;^JgOvNHorXOsr4M$cFCT|0OiC|B2=!qfSbPyE6hS;>usf!qcT z-Nb-$09IvX#@C*7o((N1>hWP)&!)r@ULt3rYXFx)82S0GC@GGu@01AbM%CmIKl$lk zr2;DiLk0@>=JeJ~k1-!5LWXOSw~ElFK49VO#Cx)cDQcg050$mNN38x78TE z4yFfF3r2||e0M*q1U{EmGrtlX%Jq7cZLv58U6VV6mJP23#NElEaZaFip z=tIqKJPb~mRNdy5+S zOt~PU-NW|<3Vm@Tyyd*XQ;8H?`_d#O_^g};`HYqV@AvQ6s>WG{R+>}42+5P`-fo?l zPl0uy0=nR8ch)(gQ67fem3xE~1(%*h-wsWa9du697E4$B@Y4HzO;pqc6 zGYS8RwpuXkmNmD1Q8xjuqp6OXTi-wJ{C3|UuIr1^&CcQL1SheGB;3smt|cey?67y&L-&erk@o|wyhV^bZJonx*3^GZ zX1dIOd)Ql~ctPSmBNL!XuezoZts#c5mQ5BvjnnsW*Zmf2LWYk;3xc!BDpcH|96Gn7 z$Kfn1!b`6+_>-my4MCjtGU6YQbPqTu1>NEu3N`6({{W&ciW50`1<~|nfD2* zS3g97fwC8^AUet5bc*5QFrWbMm0ULG%P1P@-un>dolVMvsQ_IU7n;aXO7T!1hphwk z7MURtap$b&J2prL5j7`gWgxK}$grp4cbExa+-v+W9qlQvSY*OK+gi_=qC`hP@$s_K zOQ`d9MilGx`0)JtwYOK-gYcV2=HV$RG*{>}p>o^G_R1IzVTBVNa7GE^FT9&8#)Bv8 z<0X%<^z{y6hH~i+tjWK_)!z6yUU)?%DzO}TFTOWq0*4wI-uoTJxvEzwQN1QCt~wdj zF!Ps}hbuFIl1+zNgY%pAWsca=y^IP_TE1UyV-$-PrGOb~I|QWe#XQ}WB}Q#tl)KW~ zeA2A3@q}IcLHsy(1%Xl$;cah7qcU0VEsLNn(~;KnPG3{@FRmvFe}+DP7TcM!iIp~g>^fq_xhyS1D*HWj{6gWb)em}v3GPLNoGa{t6pS-VA zx&B@*sLw!9V5!^EPgl1y&;xCJr&%IQDcA=e!?pZcuRjWa#nR6uk!ssF|MST76J1UP zWx%1ZE%5jFVi8rJ;O@kLld*{#4^>-J8FC?@!v5^Kp@qEDowhDoo_LY^rZdCXYxa;W)(HKcsv=ADu2 z`#l#BOU8?M*_OaL5h%`wPzoLWD#e*-jlJ1gTPX<2e0uWt8I9N^PK_bUEB=<;`T@h7|)0iyM`A@(fwFw zEn#OaNH3ffy=9JnqhE_LEA9Ts9uOaxD_CaM5m45^db^AS5UK*}c7Ylaj+O zu-{EO83z1}Ki5CrtiuZb#Tx$zEZ{58UD=sU+VyS8H8Gplc$@jix0s}F%3W$l0mp-~ zTjc4im<+@q_|+Vati4ys>jUgfQHkTCucF-Mum9%+~yh5Je*(*sT8>E}FOBh;@o z=Y-S__X>YzuE%Ci?CG{1PXUjeWp>#_QfF#PJwor&Eni37@o0;5z+io{EBk0XuX5^;F{i$+G1w{%O7tZ0Lob0$uj;%}1&8x8 z*BJjDq4#&`QVE|ifz6&O5~aNGEViphdxev+T`c!i@-foAE_>oTzhzzV@N6QsWI699 zVQ2!t~HAo4UdJjC?fZ{%u95-spf9mIv$h{l`W zuR9hkm<6&HSVJYU94|uaKSC*BhK7& z&pPbbTpm+FR^GYMQf#Tw2!A=$Wmg=D=9)IevX3+K0;T#;;y`eS!cyD@l=Gv!Jwg** zyk1a&mg}yxO{O?)$kC`qN?$e!;hfn05&F)c@|w@@7|^F4c%RvwGkn|y z@5uef?jB=kelil7GX6)>dB;=v|Ns9w$j(ak%C5-X;}~UT7qW${kiB;(>sS$)A%rBG z>^+L?z4tuFJl1*tUZ3ypUw^o{F4uLvp5yVjKh%{e5;?!{yno-`ZfAHqHnp*Hx;~K* zXo;L#Jqp7-vK#!4HhyW)c6(bLMbWh|k#+;35jowbZSlYuA^}#-6sQ3`HQ6E>26c~? zCW<7cbYb3b=PFZ3)!v)8dWD|t55nbhE)WQHtQf|I%;@zA!@Mn+h1}M zrEu2k9;p~#C64HqeQZ;E+sbMXiu9v7%#)Agva$C)PF<~fs=w6`65(=Gu`9EU{U=)_ zg=|e+f>^wFG=%MhSovP90rMWT13X?#fFkyGQLq_#wlwWA*Kc*LOZhvVQ#@5VutRDK z5%f-STw|7FEN(pxPUa)jy+{%bsD0gZR$VTu6w0i%8@fsXzmykT7lL_~M~g^adN;hZ z9onxIZJ`fv9={>4dOGoYu$d(j|0J#U0x=OheN#EIZS6-@wN^p!$CfFN`GeDQJ>?eB zd&IwPB{3Qp^jjgvKh9Im8z*|b8vzLb8Xf9o3Nhvmx~ z`zX??BO~6wnOrmL(;PeyZV8_;kiSiU+h)HL`6RKzkA9v;`Y;tO^WEKT zc~pbStkj~)vkSG%e)xAGkCBp6f&tI@bH3%F+0U`$=Z;^W#O64(oP5GsR)`_iVI`4a zvpREETCS$B$L&yIK6;%njE`O_39MSx&&RilVUHYVIGdrU{158QD&d!a34EN}WXo-X zBhE^G`KJ_&HF`-Mg)@eQ9WluY(= z_wlwZ@u535;!RgMDmQ%SG5b84{j<`|55#Zu&C^b3yMw$lkouBhsZ3+0wZZO7M{wmq z@ipGE6xWAifs2*blS{&Z1j`j4yd-ZkOm?nfy#*36zi6>x=;)gZ>d-EWT{r#knEeSv zZRhGcbfZHdZON0JrDMrCQZkOC1ttE99{ z64g2?H?Aa{Ki^$gV!w?L5}| zYN}2tI=V^Lp@m~WgS96&BbFcW^Oeh1^0rKbM?pO2K!#33bAugklHHs26i*JVaj)Bl z)9?RA&OL4QX-^f%zE-A;Zm^V=dZ#i#hqXGq?87(a^yjD5OcYL#FolhSIiaVW!JpCL z%+`~Bv`gD5=;7TjB%l3B*<3uFSxN9z)-5`g394>$m?fWZKjc2eHddXg;T6Z>dBV!z z>*kKg+l3qP;Jy6nXhc<}&&D;gc|bW$d&LwP18Zz#5+^{fhHz@o6miOCxyvyOf4s4B zaxm5!F_DUmYG8;sH`hBFEnzj5pikF->h5|H*&mRZdiF<9#aKIj@qVft8-jsP-)bA) z6GE*vkqRK_zusE}9s2u?-1!EorE*B%3v>b!blrD>Rwhz0z8a=)?f~T%&wa$p%-__x zjE25qRTB0>nG{l>uD;9M)k2qwf56g2c4>EC*Rn1|V5$&$+m+bY^!3qz-+hVyLz-@e zkp5!o44n+Q*WXJ6S|F8twj?l~eRnT5$O9&=6Cz;*Rnqw({tu2!=jXvx4I`-t0)b9z z+7h?pPm9lFT8$tioU+e)VuPb9lOj-)8|woNO)j32tXT>aM{EAs1KeQ3p;%f?{2e(u zQ3JviYcqUl5D0ulzkK-XNwuM%A2(hGba3Oinuc8JJTNzmL$Mk*b`IZETh;{Ya$|2w zIFBw+QL-PHaGx`1<2-ZzK|K`k6}z7jw!?g<)pnQyePIy2rv0y|dJOk@8Z|SU`u_Kz z*D~ndW+>2gnGXx?-66>?=@MC%9}VTZY76#`Cr*Y)8aul19CKs;H;N<$zo4^`GupFV zIL+(&eqvev;tyKIhk9)+r4Mo5c_RWKWA#$wJ;Mr~Zxo7;oa3Kuw}E76qpQWoA7-gH z49T=AX?oW+AYXV?5FJ-%Fjd`$AJUGlyyMCJytU~(mnYOIo)7_Nis0u9CrIdy1q=sF z9j{FS=##qv#%DD+%A+Jr?`O5a@4D3af+;GWg~;d56v|0$qP&Ndq8ZqZCqfyYeyYvZcw{H%1&a#g_k-73MM3h1oR)W1_pteC-? z+fCW0@Xo`)ZLAXzj|LEK#6b@NEKnw9glPK}^>Sm~6kNMp1-4L)^)Ok|p>O0V#xdO=dBPaq$%^%U>=MT`Y|3IZgq z^jFV$n+%SMf!JRs$tz)<0g|AEnfa%fDHj5VwEjA*eQ%c=Tc`_!lbrIAE;s}|oUr&c zHp-eNMlJMt%8~ef-+JhD*^0q`kd@HMYZJHL`Pdsv^ueTFyak|bW> zr5$7u@i~DZb$#n6#8e_!k)E^^0llm@!BWTsMgJP}oFyG8qE3*_!o*H8V1^O%3TUhB zNAMmXt-9#qzo6)q|9x#u^jJ`TRU7l-=UZuNwNcnfPg{w^+=-Pp&0hja*u`SnT01_~ z)t}sFa`$b)S~9e4$SMK8(05J=Yql4uImJ30-IVz1jG)Ig9}Qujiw*GsIYES#*n|JW zQVL=wgONJJ*a0-x<{=o0z_hNQ#~-To2AcvS)40lp3VeT&w5Tzh&iCO1iQXlObk!iP zg~z93t>-@ou(&--ck5oAJURSw$VC$^LSwi_L-P!BO-@o}p?SZ)8bS%4gYdO-NVxw< zj%f+$-t|nDY3F?LjdX{kUfR~|0M%jc4mcIMm{@#j=I z@)atUzGJV