diff --git a/src/internal/common/ptr/pointer.go b/src/internal/common/ptr/pointer.go index 68d15b109..7dbf9052f 100644 --- a/src/internal/common/ptr/pointer.go +++ b/src/internal/common/ptr/pointer.go @@ -1,13 +1,20 @@ package ptr -// Val helper method for unwrapping strings +// ptr package is a common package used for pointer +// access and deserialization. + +// Val Generic function for dereferencing pointers. // Microsoft Graph saves many variables as string pointers. // Function will safely check if the point is nil prior to // dereferencing the pointer. If the pointer is nil, -// an empty string is returned. -func Val(ptr *string) string { +// an empty version of the object is returned. +// Operation does not work on Nested objects. +// For example: +// *evt.GetEnd().GetDateTime() will still cause a panic +// if evt is nil or GetEnd() is nil +func Val[T any](ptr *T) T { if ptr == nil { - return "" + return *new(T) } return *ptr diff --git a/src/internal/common/ptr/pointer_test.go b/src/internal/common/ptr/pointer_test.go new file mode 100644 index 000000000..9cf24c5cb --- /dev/null +++ b/src/internal/common/ptr/pointer_test.go @@ -0,0 +1,99 @@ +package ptr_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" + + "github.com/alcionai/corso/src/internal/common/ptr" +) + +type PointerSuite struct { + suite.Suite +} + +func TestPointerSuite(t *testing.T) { + suite.Run(t, new(PointerSuite)) +} + +// TestVal checks to ptr derefencing for the +// following types: +// - *string +// - *bool +// - *time.Time +func (suite *PointerSuite) TestVal() { + var ( + t = suite.T() + created *time.Time + testString *string + testBool *bool + testInt *int + testInt32 *int32 + testInt64 *int64 + ) + + // String Checks + subject := ptr.Val(testString) + assert.Empty(t, subject) + + hello := "Hello World" + testString = &hello + subject = ptr.Val(testString) + + t.Logf("Received: %s", subject) + assert.NotEmpty(t, subject) + + // Time Checks + + myTime := ptr.Val(created) + assert.Empty(t, myTime) + assert.NotNil(t, myTime) + + now := time.Now() + created = &now + myTime = ptr.Val(created) + assert.NotEmpty(t, myTime) + + // Bool Checks + truth := true + myBool := ptr.Val(testBool) + assert.NotNil(t, myBool) + assert.False(t, myBool) + + testBool = &truth + myBool = ptr.Val(testBool) + assert.NotNil(t, myBool) + assert.True(t, myBool) + + // Int checks + myInt := ptr.Val(testInt) + myInt32 := ptr.Val(testInt32) + myInt64 := ptr.Val(testInt64) + + assert.NotNil(t, myInt) + assert.NotNil(t, myInt32) + assert.NotNil(t, myInt64) + assert.Empty(t, myInt) + assert.Empty(t, myInt32) + assert.Empty(t, myInt64) + + num := 4071 + num32 := int32(num * 32) + num64 := int64(num * 2048) + testInt = &num + testInt32 = &num32 + testInt64 = &num64 + + myInt = ptr.Val(testInt) + myInt32 = ptr.Val(testInt32) + myInt64 = ptr.Val(testInt64) + + assert.NotNil(t, myInt) + assert.NotNil(t, myInt32) + assert.NotNil(t, myInt64) + assert.NotEmpty(t, myInt) + assert.NotEmpty(t, myInt32) + assert.NotEmpty(t, myInt64) +} diff --git a/src/internal/connector/exchange/api/contacts.go b/src/internal/connector/exchange/api/contacts.go index 8457c94d1..1311b759f 100644 --- a/src/internal/connector/exchange/api/contacts.go +++ b/src/internal/connector/exchange/api/contacts.go @@ -3,7 +3,6 @@ package api import ( "context" "fmt" - "time" "github.com/alcionai/clues" "github.com/hashicorp/go-multierror" @@ -13,6 +12,7 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" + "github.com/alcionai/corso/src/internal/common/ptr" "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" @@ -317,16 +317,8 @@ func (c Contacts) Serialize( // --------------------------------------------------------------------------- func ContactInfo(contact models.Contactable) *details.ExchangeInfo { - name := "" - created := time.Time{} - - if contact.GetDisplayName() != nil { - name = *contact.GetDisplayName() - } - - if contact.GetCreatedDateTime() != nil { - created = *contact.GetCreatedDateTime() - } + name := ptr.Val(contact.GetDisplayName()) + created := ptr.Val(contact.GetCreatedDateTime()) return &details.ExchangeInfo{ ItemType: details.ExchangeContact, diff --git a/src/internal/connector/exchange/api/events.go b/src/internal/connector/exchange/api/events.go index adf218685..4097c6d0f 100644 --- a/src/internal/connector/exchange/api/events.go +++ b/src/internal/connector/exchange/api/events.go @@ -14,6 +14,7 @@ import ( "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/common" + "github.com/alcionai/corso/src/internal/common/ptr" "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" @@ -390,11 +391,12 @@ func (c CalendarDisplayable) GetParentFolderId() *string { func EventInfo(evt models.Eventable) *details.ExchangeInfo { var ( - organizer, subject string - recurs bool - start = time.Time{} - end = time.Time{} - created = time.Time{} + organizer string + subject = ptr.Val(evt.GetSubject()) + recurs bool + start = time.Time{} + end = time.Time{} + created = ptr.Val(evt.GetCreatedDateTime()) ) if evt.GetOrganizer() != nil && @@ -405,10 +407,6 @@ func EventInfo(evt models.Eventable) *details.ExchangeInfo { GetAddress() } - if evt.GetSubject() != nil { - subject = *evt.GetSubject() - } - if evt.GetRecurrence() != nil { recurs = true } @@ -437,10 +435,6 @@ func EventInfo(evt models.Eventable) *details.ExchangeInfo { } } - if evt.GetCreatedDateTime() != nil { - created = *evt.GetCreatedDateTime() - } - return &details.ExchangeInfo{ ItemType: details.ExchangeEvent, Organizer: organizer, diff --git a/src/internal/connector/exchange/api/mail.go b/src/internal/connector/exchange/api/mail.go index 5ac96b93a..dd6702325 100644 --- a/src/internal/connector/exchange/api/mail.go +++ b/src/internal/connector/exchange/api/mail.go @@ -3,7 +3,6 @@ package api import ( "context" "fmt" - "time" "github.com/alcionai/clues" "github.com/hashicorp/go-multierror" @@ -13,6 +12,7 @@ import ( "github.com/microsoftgraph/msgraph-sdk-go/users" "github.com/pkg/errors" + "github.com/alcionai/corso/src/internal/common/ptr" "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" @@ -348,9 +348,9 @@ func (c Mail) Serialize( func MailInfo(msg models.Messageable) *details.ExchangeInfo { sender := "" - subject := "" - received := time.Time{} - created := time.Time{} + subject := ptr.Val(msg.GetSubject()) + received := ptr.Val(msg.GetReceivedDateTime()) + created := ptr.Val(msg.GetCreatedDateTime()) if msg.GetSender() != nil && msg.GetSender().GetEmailAddress() != nil && @@ -358,18 +358,6 @@ func MailInfo(msg models.Messageable) *details.ExchangeInfo { sender = *msg.GetSender().GetEmailAddress().GetAddress() } - if msg.GetSubject() != nil { - subject = *msg.GetSubject() - } - - if msg.GetReceivedDateTime() != nil { - received = *msg.GetReceivedDateTime() - } - - if msg.GetCreatedDateTime() != nil { - created = *msg.GetCreatedDateTime() - } - return &details.ExchangeInfo{ ItemType: details.ExchangeMail, Sender: sender, diff --git a/src/internal/connector/exchange/attachment.go b/src/internal/connector/exchange/attachment.go index ed8828930..b8a29dfe2 100644 --- a/src/internal/connector/exchange/attachment.go +++ b/src/internal/connector/exchange/attachment.go @@ -25,7 +25,8 @@ const ( ) func attachmentType(attachment models.Attachmentable) models.AttachmentType { - switch *attachment.GetOdataType() { + attachmentType := ptr.Val(attachment.GetOdataType()) + switch attachmentType { case fileAttachmentOdataValue: return models.FILE_ATTACHMENTTYPE case itemAttachmentOdataValue: diff --git a/src/internal/connector/exchange/service_restore.go b/src/internal/connector/exchange/service_restore.go index bb0179c76..68faa57e0 100644 --- a/src/internal/connector/exchange/service_restore.go +++ b/src/internal/connector/exchange/service_restore.go @@ -11,6 +11,7 @@ import ( "github.com/pkg/errors" "github.com/alcionai/corso/src/internal/common" + "github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/connector/exchange/api" "github.com/alcionai/corso/src/internal/connector/graph" "github.com/alcionai/corso/src/internal/connector/support" @@ -71,7 +72,7 @@ func RestoreExchangeContact( response, err := service.Client().UsersById(user).ContactFoldersById(destination).Contacts().Post(ctx, contact, nil) if err != nil { - name := *contact.GetGivenName() + name := ptr.Val(contact.GetGivenName()) return nil, errors.Wrap( err, @@ -146,7 +147,8 @@ func RestoreExchangeEvent( errs = support.WrapAndAppend( fmt.Sprintf( "uploading attachment for message %s: %s", - *transformedEvent.GetId(), support.ConnectorStackErrorTrace(err), + ptr.Val(transformedEvent.GetId()), + support.ConnectorStackErrorTrace(err), ), err, errs, @@ -283,12 +285,8 @@ func SendMailToBackStore( for _, attachment := range attached { if err := uploadAttachment(ctx, uploader, attachment); err != nil { - if attachment.GetOdataType() != nil && - *attachment.GetOdataType() == "#microsoft.graph.itemAttachment" { - var name string - if attachment.GetName() != nil { - name = *attachment.GetName() - } + if ptr.Val(attachment.GetOdataType()) == "#microsoft.graph.itemAttachment" { + name := ptr.Val(attachment.GetName()) logger.Ctx(ctx).Infow( "item attachment upload not successful. content not accepted by M365 server",