diff --git a/src/pkg/services/m365/api/consts.go b/src/pkg/services/m365/api/consts.go index 2e9f48f6a..96778c532 100644 --- a/src/pkg/services/m365/api/consts.go +++ b/src/pkg/services/m365/api/consts.go @@ -1,5 +1,9 @@ package api +import ( + "github.com/alcionai/corso/src/internal/common/keys" +) + // Well knwon Folder Names // Mail Definitions: https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0 const ( @@ -11,3 +15,82 @@ const ( // Kiota JSON invalid JSON error message. invalidJSON = "invalid json type" ) + +// ************** Lists starts ***************** + +const ( + AttachmentsColumnName = "Attachments" + EditColumnName = "Edit" + ContentTypeColumnName = "ContentType" + CreatedColumnName = "Created" + ModifiedColumnName = "Modified" + AuthorLookupIDColumnName = "AuthorLookupId" + EditorLookupIDColumnName = "EditorLookupId" + AppAuthorLookupIDColumnName = "AppAuthorLookupId" + TitleColumnName = "Title" + + ContentTypeColumnDisplayName = "Content Type" + + AddressKey = "address" + CoordinatesKey = "coordinates" + DisplayNameKey = "displayName" + LocationURIKey = "locationUri" + UniqueIDKey = "uniqueId" + + // entries that are nested within a second layer + CityKey = "city" + CountryKey = "countryOrRegion" + PostalCodeKey = "postalCode" + StateKey = "state" + StreetKey = "street" + LatitudeKey = "latitude" + LongitudeKey = "longitude" + + CountryOrRegionFN = "CountryOrRegion" + StateFN = "State" + CityFN = "City" + PostalCodeFN = "PostalCode" + StreetFN = "Street" + GeoLocFN = "GeoLoc" + DispNameFN = "DispName" + + HyperlinkDescriptionKey = "Description" + HyperlinkURLKey = "Url" + + LinkTitleFieldNamePart = "LinkTitle" + ChildCountFieldNamePart = "ChildCount" + LookupIDFieldNamePart = "LookupId" + + ReadOnlyOrHiddenFieldNamePrefix = "_" + DescoratorFieldNamePrefix = "@" + + WebTemplateExtensionsListTemplate = "webTemplateExtensionsList" + // This issue https://github.com/alcionai/corso/issues/4932 + // tracks to backup/restore supportability of `documentLibrary` templated lists + DocumentLibraryListTemplate = "documentLibrary" + SharingLinksListTemplate = "sharingLinks" + AccessRequestsListTemplate = "accessRequest" +) + +var addressFieldNames = []string{ + AddressKey, + CoordinatesKey, + DisplayNameKey, + LocationURIKey, + UniqueIDKey, +} + +var legacyColumns = keys.Set{ + AttachmentsColumnName: {}, + EditColumnName: {}, + ContentTypeColumnDisplayName: {}, +} + +var SkipListTemplates = keys.Set{ + WebTemplateExtensionsListTemplate: {}, + DocumentLibraryListTemplate: {}, + SharingLinksListTemplate: {}, + AccessRequestsListTemplate: {}, +} + +// ************** Lists ends ***************** diff --git a/src/pkg/services/m365/api/lists.go b/src/pkg/services/m365/api/lists.go index c8a3f7470..4a0809152 100644 --- a/src/pkg/services/m365/api/lists.go +++ b/src/pkg/services/m365/api/lists.go @@ -18,78 +18,6 @@ import ( var ErrSkippableListTemplate = clues.New("unable to create lists with skippable templates") -const ( - AttachmentsColumnName = "Attachments" - EditColumnName = "Edit" - ContentTypeColumnName = "ContentType" - CreatedColumnName = "Created" - ModifiedColumnName = "Modified" - AuthorLookupIDColumnName = "AuthorLookupId" - EditorLookupIDColumnName = "EditorLookupId" - AppAuthorLookupIDColumnName = "AppAuthorLookupId" - TitleColumnName = "Title" - - ContentTypeColumnDisplayName = "Content Type" - - AddressKey = "address" - CoordinatesKey = "coordinates" - DisplayNameKey = "displayName" - LocationURIKey = "locationUri" - UniqueIDKey = "uniqueId" - - // entries that are nested within a second layer - CityKey = "city" - CountryKey = "countryOrRegion" - PostalCodeKey = "postalCode" - StateKey = "state" - StreetKey = "street" - LatitudeKey = "latitude" - LongitudeKey = "longitude" - - CountryOrRegionFN = "CountryOrRegion" - StateFN = "State" - CityFN = "City" - PostalCodeFN = "PostalCode" - StreetFN = "Street" - GeoLocFN = "GeoLoc" - DispNameFN = "DispName" - - LinkTitleFieldNamePart = "LinkTitle" - ChildCountFieldNamePart = "ChildCount" - LookupIDFieldNamePart = "LookupId" - - ReadOnlyOrHiddenFieldNamePrefix = "_" - DescoratorFieldNamePrefix = "@" - - WebTemplateExtensionsListTemplate = "webTemplateExtensionsList" - // This issue https://github.com/alcionai/corso/issues/4932 - // tracks to backup/restore supportability of `documentLibrary` templated lists - DocumentLibraryListTemplate = "documentLibrary" - SharingLinksListTemplate = "sharingLinks" - AccessRequestsListTemplate = "accessRequest" -) - -var addressFieldNames = []string{ - AddressKey, - CoordinatesKey, - DisplayNameKey, - LocationURIKey, - UniqueIDKey, -} - -var legacyColumns = keys.Set{ - AttachmentsColumnName: {}, - EditColumnName: {}, - ContentTypeColumnDisplayName: {}, -} - -var SkipListTemplates = keys.Set{ - WebTemplateExtensionsListTemplate: {}, - DocumentLibraryListTemplate: {}, - SharingLinksListTemplate: {}, - AccessRequestsListTemplate: {}, -} - // --------------------------------------------------------------------------- // controller // --------------------------------------------------------------------------- @@ -507,6 +435,11 @@ func retrieveFieldData(orig models.FieldValueSetable, columnNames map[string]any additionalData[fieldName] = concatenatedAddress } + if hyperLinkField, fieldName, ok := hasHyperLinkFields(additionalData); ok { + concatenatedHyperlink := concatenateHyperLinkFields(hyperLinkField) + additionalData[fieldName] = concatenatedHyperlink + } + fields.SetAdditionalData(additionalData) return fields @@ -547,6 +480,22 @@ func hasAddressFields(additionalData map[string]any) (map[string]any, string, bo return nil, "", false } +func hasHyperLinkFields(additionalData map[string]any) (map[string]any, string, bool) { + for fieldName, value := range additionalData { + nestedFields, ok := value.(map[string]any) + if !ok { + continue + } + + if keys.HasKeys(nestedFields, + []string{HyperlinkDescriptionKey, HyperlinkURLKey}...) { + return nestedFields, fieldName, true + } + } + + return nil, "", false +} + func concatenateAddressFields(addressFields map[string]any) string { parts := make([]string, 0) @@ -574,6 +523,24 @@ func concatenateAddressFields(addressFields map[string]any) string { return "" } +func concatenateHyperLinkFields(hyperlinkFields map[string]any) string { + parts := make([]string, 0) + + if v, err := str.AnyValueToString(HyperlinkURLKey, hyperlinkFields); err == nil { + parts = append(parts, v) + } + + if v, err := str.AnyValueToString(HyperlinkDescriptionKey, hyperlinkFields); err == nil { + parts = append(parts, v) + } + + if len(parts) > 0 { + return strings.Join(parts, ",") + } + + return "" +} + func addressKeyToVal(fields map[string]any, key string) string { if v, err := str.AnyValueToString(key, fields); err == nil { return v diff --git a/src/pkg/services/m365/api/lists_test.go b/src/pkg/services/m365/api/lists_test.go index 18968f9d6..ef1195b75 100644 --- a/src/pkg/services/m365/api/lists_test.go +++ b/src/pkg/services/m365/api/lists_test.go @@ -650,6 +650,59 @@ func (suite *ListsUnitSuite) TestHasAddressFields() { } } +func (suite *ListsUnitSuite) TestConcatenateHyperlinkFields() { + t := suite.T() + + tests := []struct { + name string + hyperlinkFields map[string]any + expectedResult string + }{ + { + name: "Valid Hyperlink", + hyperlinkFields: map[string]any{ + HyperlinkURLKey: ptr.To("https://www.example.com"), + HyperlinkDescriptionKey: ptr.To("Example Website"), + }, + expectedResult: "https://www.example.com,Example Website", + }, + { + name: "Empty Hyperlink Fields", + hyperlinkFields: map[string]any{ + HyperlinkURLKey: nil, + HyperlinkDescriptionKey: nil, + }, + expectedResult: "", + }, + { + name: "Missing Description", + hyperlinkFields: map[string]any{ + HyperlinkURLKey: ptr.To("https://www.example.com"), + }, + expectedResult: "https://www.example.com", + }, + { + name: "Missing URL", + hyperlinkFields: map[string]any{ + HyperlinkDescriptionKey: ptr.To("Example Website"), + }, + expectedResult: "Example Website", + }, + { + name: "Empty Input", + hyperlinkFields: map[string]any{}, + expectedResult: "", + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + result := concatenateHyperLinkFields(test.hyperlinkFields) + assert.Equal(t, test.expectedResult, result) + }) + } +} + type ListsAPIIntgSuite struct { tester.Suite its intgTesterSetup