diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7083e885b..170a63357 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/src/internal/connector/mockconnector/mock_data_collection_test.go b/src/internal/connector/mockconnector/mock_data_collection_test.go index 2af9236c4..f2ba4d08e 100644 --- a/src/internal/connector/mockconnector/mock_data_collection_test.go +++ b/src/internal/connector/mockconnector/mock_data_collection_test.go @@ -202,6 +202,15 @@ func (suite *MockExchangeDataSuite) TestMockByteHydration() { return err }, }, + { + name: "SharePoint: Page", + transformation: func(t *testing.T) error { + bytes := mockconnector.GetMockPage(subject) + _, err := support.CreatePageFromBytes(bytes) + + return err + }, + }, } for _, test := range tests { diff --git a/src/internal/connector/mockconnector/mock_data_page.go b/src/internal/connector/mockconnector/mock_data_page.go new file mode 100644 index 000000000..0b8425418 --- /dev/null +++ b/src/internal/connector/mockconnector/mock_data_page.go @@ -0,0 +1,25 @@ +package mockconnector + +// GetMockPage returns bytes for models.SitePageable object +// Title string changes of fields: name and title +func GetMockPage(title string) []byte { + fileName := title + ".aspx" + + // Create Test Page + //nolint:lll + byteArray := []byte("{\"name\":\"" + fileName + "\",\"title\":\"" + title + "\",\"pageLayout\":\"article\",\"showComments\":true," + + "\"showRecommendedPages\":false,\"titleArea\":{\"enableGradientEffect\":true,\"imageWebUrl\":\"/_LAYOUTS/IMAGES/VISUALTEMPLATETITLEIMAGE.JPG\"," + + "\"layout\":\"colorBlock\",\"showAuthor\":true,\"showPublishedDate\":false,\"showTextBlockAboveTitle\":false,\"textAboveTitle\":\"TEXTABOVETITLE\"," + + "\"textAlignment\":\"left\",\"imageSourceType\":2,\"title\":\"sample1\"}," + + "\"canvasLayout\":{\"horizontalSections\":[{\"layout\":\"oneThirdRightColumn\",\"id\":\"1\",\"emphasis\":\"none\",\"columns\":[{\"id\":\"1\",\"width\":8," + + "\"webparts\":[{\"id\":\"6f9230af-2a98-4952-b205-9ede4f9ef548\",\"innerHtml\":\"
Hello!
\"}]},{\"id\":\"2\",\"width\":4," + + "\"webparts\":[{\"id\":\"73d07dde-3474-4545-badb-f28ba239e0e1\",\"webPartType\":\"d1d91016-032f-456d-98a4-721247c305e8\",\"data\":{\"dataVersion\":\"1.9\"," + + "\"description\":\"Showanimageonyourpage\",\"title\":\"Image\",\"properties\":{\"imageSourceType\":2,\"altText\":\"\",\"overlayText\":\"\"," + + "\"siteid\":\"0264cabe-6b92-450a-b162-b0c3d54fe5e8\",\"webid\":\"f3989670-cd37-4514-8ccb-0f7c2cbe5314\",\"listid\":\"bdb41041-eb06-474e-ac29-87093386bb14\"," + + "\"uniqueid\":\"d9f94b40-78ba-48d0-a39f-3cb23c2fe7eb\",\"imgWidth\":4288,\"imgHeight\":2848,\"fixAspectRatio\":false,\"captionText\":\"\",\"alignment\":\"Center\"}," + + "\"serverProcessedContent\":{\"imageSources\":[{\"key\":\"imageSource\",\"value\":\"/_LAYOUTS/IMAGES/VISUALTEMPLATEIMAGE1.JPG\"}]," + + "\"customMetadata\":[{\"key\":\"imageSource\",\"value\":{\"siteid\":\"0264cabe-6b92-450a-b162-b0c3d54fe5e8\",\"webid\":\"f3989670-cd37-4514-8ccb-0f7c2cbe5314\"," + + "\"listid\":\"bdb41041-eb06-474e-ac29-87093386bb14\",\"uniqueid\":\"d9f94b40-78ba-48d0-a39f-3cb23c2fe7eb\",\"width\":\"4288\",\"height\":\"2848\"}}]}}}]}]}]}}") + + return byteArray +} diff --git a/src/internal/connector/support/m365Support.go b/src/internal/connector/support/m365Support.go index d7e51e513..0780a2b0e 100644 --- a/src/internal/connector/support/m365Support.go +++ b/src/internal/connector/support/m365Support.go @@ -1,6 +1,9 @@ package support import ( + "strings" + + bmodels "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" absser "github.com/microsoft/kiota-abstractions-go/serialization" js "github.com/microsoft/kiota-serialization-json-go" "github.com/microsoftgraph/msgraph-sdk-go/models" @@ -12,7 +15,7 @@ import ( func CreateFromBytes(bytes []byte, createFunc absser.ParsableFactory) (absser.Parsable, error) { parseNode, err := js.NewJsonParseNodeFactory().GetRootParseNode("application/json", bytes) if err != nil { - return nil, errors.Wrap(err, "parsing byte array into m365 object") + return nil, errors.Wrap(err, "deserializing bytes into base m365 object") } anObject, err := parseNode.GetObjectValue(createFunc) @@ -27,7 +30,7 @@ func CreateFromBytes(bytes []byte, createFunc absser.ParsableFactory) (absser.Pa func CreateMessageFromBytes(bytes []byte) (models.Messageable, error) { aMessage, err := CreateFromBytes(bytes, models.CreateMessageFromDiscriminatorValue) if err != nil { - return nil, errors.Wrap(err, "creating m365 exchange.Mail object from provided bytes") + return nil, errors.Wrap(err, "deserializing bytes to exchange message") } message := aMessage.(models.Messageable) @@ -40,7 +43,7 @@ func CreateMessageFromBytes(bytes []byte) (models.Messageable, error) { func CreateContactFromBytes(bytes []byte) (models.Contactable, error) { parsable, err := CreateFromBytes(bytes, models.CreateContactFromDiscriminatorValue) if err != nil { - return nil, errors.Wrap(err, "creating m365 exchange.Contact object from provided bytes") + return nil, errors.Wrap(err, "deserializing bytes to exchange contact") } contact := parsable.(models.Contactable) @@ -52,7 +55,7 @@ func CreateContactFromBytes(bytes []byte) (models.Contactable, error) { func CreateEventFromBytes(bytes []byte) (models.Eventable, error) { parsable, err := CreateFromBytes(bytes, models.CreateEventFromDiscriminatorValue) if err != nil { - return nil, errors.Wrap(err, "creating m365 exchange.Event object from provided bytes") + return nil, errors.Wrap(err, "deserializing bytes to exchange event") } event := parsable.(models.Eventable) @@ -64,10 +67,33 @@ func CreateEventFromBytes(bytes []byte) (models.Eventable, error) { func CreateListFromBytes(bytes []byte) (models.Listable, error) { parsable, err := CreateFromBytes(bytes, models.CreateListFromDiscriminatorValue) if err != nil { - return nil, errors.Wrap(err, "creating m365 sharepoint.List object from provided bytes") + return nil, errors.Wrap(err, "deserializing bytes to sharepoint list") } list := parsable.(models.Listable) return list, nil } + +// CreatePageFromBytes transforms given bytes in models.SitePageable object +func CreatePageFromBytes(bytes []byte) (bmodels.SitePageable, error) { + parsable, err := CreateFromBytes(bytes, bmodels.CreateSitePageFromDiscriminatorValue) + if err != nil { + return nil, errors.Wrap(err, "deserializing bytes to sharepoint page") + } + + page := parsable.(bmodels.SitePageable) + + return page, nil +} + +func HasAttachments(body models.ItemBodyable) bool { + if body.GetContent() == nil || body.GetContentType() == nil || + *body.GetContentType() == models.TEXT_BODYTYPE || len(*body.GetContent()) == 0 { + return false + } + + content := *body.GetContent() + + return strings.Contains(content, "src=\"cid:") +} diff --git a/src/internal/connector/support/m365Support_test.go b/src/internal/connector/support/m365Support_test.go index c04c74604..946996431 100644 --- a/src/internal/connector/support/m365Support_test.go +++ b/src/internal/connector/support/m365Support_test.go @@ -3,10 +3,13 @@ package support import ( "testing" + kioser "github.com/microsoft/kiota-serialization-json-go" + "github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" + bmodels "github.com/alcionai/corso/src/internal/connector/graph/betasdk/models" "github.com/alcionai/corso/src/internal/connector/mockconnector" ) @@ -18,6 +21,11 @@ func TestDataSupportSuite(t *testing.T) { suite.Run(t, new(DataSupportSuite)) } +var ( + empty = "Empty Bytes" + invalid = "Invalid Bytes" +) + // TestCreateMessageFromBytes verifies approved mockdata bytes can // be successfully transformed into M365 Message data. func (suite *DataSupportSuite) TestCreateMessageFromBytes() { @@ -59,13 +67,13 @@ func (suite *DataSupportSuite) TestCreateContactFromBytes() { isNil assert.ValueAssertionFunc }{ { - name: "Empty Bytes", + name: empty, byteArray: make([]byte, 0), checkError: assert.Error, isNil: assert.Nil, }, { - name: "Invalid Bytes", + name: invalid, byteArray: []byte("A random sentence doesn't make an object"), checkError: assert.Error, isNil: assert.Nil, @@ -94,13 +102,13 @@ func (suite *DataSupportSuite) TestCreateEventFromBytes() { isNil assert.ValueAssertionFunc }{ { - name: "Empty Byes", + name: empty, byteArray: make([]byte, 0), checkError: assert.Error, isNil: assert.Nil, }, { - name: "Invalid Bytes", + name: invalid, byteArray: []byte("Invalid byte stream \"subject:\" Not going to work"), checkError: assert.Error, isNil: assert.Nil, @@ -132,13 +140,13 @@ func (suite *DataSupportSuite) TestCreateListFromBytes() { isNil assert.ValueAssertionFunc }{ { - name: "Empty Byes", + name: empty, byteArray: make([]byte, 0), checkError: assert.Error, isNil: assert.Nil, }, { - name: "Invalid Bytes", + name: invalid, byteArray: []byte("Invalid byte stream \"subject:\" Not going to work"), checkError: assert.Error, isNil: assert.Nil, @@ -159,3 +167,111 @@ func (suite *DataSupportSuite) TestCreateListFromBytes() { }) } } + +func (suite *DataSupportSuite) TestCreatePageFromBytes() { + tests := []struct { + name string + checkError assert.ErrorAssertionFunc + isNil assert.ValueAssertionFunc + getBytes func(t *testing.T) []byte + }{ + { + empty, + assert.Error, + assert.Nil, + func(t *testing.T) []byte { + return make([]byte, 0) + }, + }, + { + invalid, + assert.Error, + assert.Nil, + func(t *testing.T) []byte { + return []byte("snarf") + }, + }, + { + "Valid Page", + assert.NoError, + assert.NotNil, + func(t *testing.T) []byte { + pg := bmodels.NewSitePage() + title := "Tested" + pg.SetTitle(&title) + pg.SetName(&title) + pg.SetWebUrl(&title) + + writer := kioser.NewJsonSerializationWriter() + err := pg.Serialize(writer) + require.NoError(t, err) + + byteArray, err := writer.GetSerializedContent() + require.NoError(t, err) + + return byteArray + }, + }, + } + + for _, test := range tests { + suite.T().Run(test.name, func(t *testing.T) { + result, err := CreatePageFromBytes(test.getBytes(t)) + test.checkError(t, err) + test.isNil(t, result) + }) + } +} + +func (suite *DataSupportSuite) TestHasAttachments() { + tests := []struct { + name string + hasAttachment assert.BoolAssertionFunc + getBodyable func(t *testing.T) models.ItemBodyable + }{ + { + name: "Mock w/out attachment", + hasAttachment: assert.False, + getBodyable: func(t *testing.T) models.ItemBodyable { + byteArray := mockconnector.GetMockMessageWithBodyBytes( + "Test", + "This is testing", + "This is testing", + ) + message, err := CreateMessageFromBytes(byteArray) + require.NoError(t, err) + return message.GetBody() + }, + }, + { + name: "Mock w/ inline attachment", + hasAttachment: assert.True, + getBodyable: func(t *testing.T) models.ItemBodyable { + byteArray := mockconnector.GetMessageWithOneDriveAttachment("Test legacy") + message, err := CreateMessageFromBytes(byteArray) + require.NoError(t, err) + return message.GetBody() + }, + }, + { + name: "Edge Case", + hasAttachment: assert.True, + getBodyable: func(t *testing.T) models.ItemBodyable { + //nolint:lll + content := "\r\n