handles hyperlink columns and fields (#4971)

Hyperlink column of a list item is not identifiable from GRAPH's
response.
Hence this is a workaround to handle such fields until a column
definition is introduced for `Hyperlink`.

#### Does this PR need a docs update or release note?
- [x]  No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature

#### Issue(s)
#4754 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x]  Unit test
- [x] 💚 E2E
This commit is contained in:
Hitesh Pattanayak 2024-01-13 13:07:28 +05:30 committed by GitHub
parent 06afd53660
commit 8989fcd7cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 175 additions and 72 deletions

View File

@ -1,5 +1,9 @@
package api package api
import (
"github.com/alcionai/corso/src/internal/common/keys"
)
// Well knwon Folder Names // Well knwon Folder Names
// Mail Definitions: https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0 // Mail Definitions: https://docs.microsoft.com/en-us/graph/api/resources/mailfolder?view=graph-rest-1.0
const ( const (
@ -11,3 +15,82 @@ const (
// Kiota JSON invalid JSON error message. // Kiota JSON invalid JSON error message.
invalidJSON = "invalid json type" 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 *****************

View File

@ -18,78 +18,6 @@ import (
var ErrSkippableListTemplate = clues.New("unable to create lists with skippable templates") 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 // controller
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@ -507,6 +435,11 @@ func retrieveFieldData(orig models.FieldValueSetable, columnNames map[string]any
additionalData[fieldName] = concatenatedAddress additionalData[fieldName] = concatenatedAddress
} }
if hyperLinkField, fieldName, ok := hasHyperLinkFields(additionalData); ok {
concatenatedHyperlink := concatenateHyperLinkFields(hyperLinkField)
additionalData[fieldName] = concatenatedHyperlink
}
fields.SetAdditionalData(additionalData) fields.SetAdditionalData(additionalData)
return fields return fields
@ -547,6 +480,22 @@ func hasAddressFields(additionalData map[string]any) (map[string]any, string, bo
return nil, "", false 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 { func concatenateAddressFields(addressFields map[string]any) string {
parts := make([]string, 0) parts := make([]string, 0)
@ -574,6 +523,24 @@ func concatenateAddressFields(addressFields map[string]any) string {
return "" 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 { func addressKeyToVal(fields map[string]any, key string) string {
if v, err := str.AnyValueToString(key, fields); err == nil { if v, err := str.AnyValueToString(key, fields); err == nil {
return v return v

View File

@ -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 { type ListsAPIIntgSuite struct {
tester.Suite tester.Suite
its intgTesterSetup its intgTesterSetup