handles location columns and fields (#4970)

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

#### 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

---------

Co-authored-by: aviator-app[bot] <48659329+aviator-app[bot]@users.noreply.github.com>
This commit is contained in:
Hitesh Pattanayak 2024-01-13 11:35:28 +05:30 committed by GitHub
parent b459d27cc7
commit 06afd53660
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 265 additions and 52 deletions

View File

@ -2,12 +2,15 @@ package api
import ( import (
"context" "context"
"fmt"
"strings"
"github.com/alcionai/clues" "github.com/alcionai/clues"
"github.com/microsoftgraph/msgraph-sdk-go/models" "github.com/microsoftgraph/msgraph-sdk-go/models"
"github.com/alcionai/corso/src/internal/common/keys" "github.com/alcionai/corso/src/internal/common/keys"
"github.com/alcionai/corso/src/internal/common/ptr" "github.com/alcionai/corso/src/internal/common/ptr"
"github.com/alcionai/corso/src/internal/common/str"
"github.com/alcionai/corso/src/pkg/backup/details" "github.com/alcionai/corso/src/pkg/backup/details"
"github.com/alcionai/corso/src/pkg/fault" "github.com/alcionai/corso/src/pkg/fault"
"github.com/alcionai/corso/src/pkg/services/m365/api/graph" "github.com/alcionai/corso/src/pkg/services/m365/api/graph"
@ -28,22 +31,32 @@ const (
ContentTypeColumnDisplayName = "Content Type" ContentTypeColumnDisplayName = "Content Type"
AddressFieldName = "address" AddressKey = "address"
CoordinatesFieldName = "coordinates" CoordinatesKey = "coordinates"
DisplayNameFieldName = "displayName" DisplayNameKey = "displayName"
LocationURIFieldName = "locationUri" LocationURIKey = "locationUri"
UniqueIDFieldName = "uniqueId" UniqueIDKey = "uniqueId"
CountryOrRegionFieldName = "CountryOrRegion" // entries that are nested within a second layer
StateFieldName = "State" CityKey = "city"
CityFieldName = "City" CountryKey = "countryOrRegion"
PostalCodeFieldName = "PostalCode" PostalCodeKey = "postalCode"
StreetFieldName = "Street" StateKey = "state"
GeoLocFieldName = "GeoLoc" StreetKey = "street"
DispNameFieldName = "DispName" LatitudeKey = "latitude"
LinkTitleFieldNamePart = "LinkTitle" LongitudeKey = "longitude"
ChildCountFieldNamePart = "ChildCount"
LookupIDFieldNamePart = "LookupId" CountryOrRegionFN = "CountryOrRegion"
StateFN = "State"
CityFN = "City"
PostalCodeFN = "PostalCode"
StreetFN = "Street"
GeoLocFN = "GeoLoc"
DispNameFN = "DispName"
LinkTitleFieldNamePart = "LinkTitle"
ChildCountFieldNamePart = "ChildCount"
LookupIDFieldNamePart = "LookupId"
ReadOnlyOrHiddenFieldNamePrefix = "_" ReadOnlyOrHiddenFieldNamePrefix = "_"
DescoratorFieldNamePrefix = "@" DescoratorFieldNamePrefix = "@"
@ -56,6 +69,14 @@ const (
AccessRequestsListTemplate = "accessRequest" AccessRequestsListTemplate = "accessRequest"
) )
var addressFieldNames = []string{
AddressKey,
CoordinatesKey,
DisplayNameKey,
LocationURIKey,
UniqueIDKey,
}
var legacyColumns = keys.Set{ var legacyColumns = keys.Set{
AttachmentsColumnName: {}, AttachmentsColumnName: {},
EditColumnName: {}, EditColumnName: {},
@ -481,6 +502,11 @@ func retrieveFieldData(orig models.FieldValueSetable, columnNames map[string]any
fields := models.NewFieldValueSet() fields := models.NewFieldValueSet()
additionalData := setAdditionalDataByColumnNames(orig, columnNames) additionalData := setAdditionalDataByColumnNames(orig, columnNames)
if addressField, fieldName, ok := hasAddressFields(additionalData); ok {
concatenatedAddress := concatenateAddressFields(addressField)
additionalData[fieldName] = concatenatedAddress
}
fields.SetAdditionalData(additionalData) fields.SetAdditionalData(additionalData)
return fields return fields
@ -506,6 +532,58 @@ func setAdditionalDataByColumnNames(
return filteredData return filteredData
} }
func hasAddressFields(additionalData map[string]any) (map[string]any, string, bool) {
for k, v := range additionalData {
nestedFields, ok := v.(map[string]any)
if !ok || keys.HasKeys(nestedFields, GeoLocFN) {
continue
}
if keys.HasKeys(nestedFields, addressFieldNames...) {
return nestedFields, k, true
}
}
return nil, "", false
}
func concatenateAddressFields(addressFields map[string]any) string {
parts := make([]string, 0)
if dispName, ok := addressFields[DisplayNameKey].(*string); ok {
parts = append(parts, ptr.Val(dispName))
}
if fields, ok := addressFields[AddressKey].(map[string]any); ok {
parts = append(parts, addressKeyToVal(fields, StreetKey))
parts = append(parts, addressKeyToVal(fields, CityKey))
parts = append(parts, addressKeyToVal(fields, StateKey))
parts = append(parts, addressKeyToVal(fields, CountryKey))
parts = append(parts, addressKeyToVal(fields, PostalCodeKey))
}
if coords, ok := addressFields[CoordinatesKey].(map[string]any); ok {
parts = append(parts, addressKeyToVal(coords, LatitudeKey))
parts = append(parts, addressKeyToVal(coords, LongitudeKey))
}
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
} else if v, ok := fields[key].(*float64); ok {
return fmt.Sprintf("%v", ptr.Val(v))
}
return ""
}
func (c Lists) getListItemFields( func (c Lists) getListItemFields(
ctx context.Context, ctx context.Context,
siteID, listID, itemID string, siteID, listID, itemID string,

View File

@ -1,6 +1,7 @@
package api package api
import ( import (
"fmt"
"testing" "testing"
"time" "time"
@ -455,52 +456,54 @@ func (suite *ListsUnitSuite) TestFieldValueSetable() {
func (suite *ListsUnitSuite) TestFieldValueSetable_Location() { func (suite *ListsUnitSuite) TestFieldValueSetable_Location() {
t := suite.T() t := suite.T()
displayName := "B123 Unit 1852 Prime Residences Tagaytay"
street := "Prime Residences CityLand 1852"
state := "Calabarzon"
postal := "4120"
country := "Philippines"
city := "Tagaytay"
lat := 14.1153
lon := 120.962
additionalData := map[string]any{ additionalData := map[string]any{
"MyAddress": map[string]any{ "MyAddress": map[string]any{
AddressFieldName: map[string]any{ AddressKey: map[string]any{
"city": "Tagaytay", CityKey: ptr.To(city),
"countryOrRegion": "Philippines", CountryKey: ptr.To(country),
"postalCode": "4120", PostalCodeKey: ptr.To(postal),
"state": "Calabarzon", StateKey: ptr.To(state),
"street": "Prime Residences CityLand 1852", StreetKey: ptr.To(street),
}, },
CoordinatesFieldName: map[string]any{ CoordinatesKey: map[string]any{
"latitude": "14.1153", LatitudeKey: ptr.To(lat),
"longitude": "120.962", LongitudeKey: ptr.To(lon),
}, },
DisplayNameFieldName: "B123 Unit 1852 Prime Residences Tagaytay", DisplayNameKey: ptr.To(displayName),
LocationURIFieldName: "https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032", LocationURIKey: ptr.To("https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032"),
UniqueIDFieldName: "https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032", UniqueIDKey: ptr.To("https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032"),
}, },
CountryOrRegionFieldName: "Philippines", CountryOrRegionFN: ptr.To(country),
StateFieldName: "Calabarzon", StateFN: ptr.To(state),
CityFieldName: "Tagaytay", CityFN: ptr.To(city),
PostalCodeFieldName: "4120", PostalCodeFN: ptr.To(postal),
StreetFieldName: "Prime Residences CityLand 1852", StreetFN: ptr.To(street),
GeoLocFieldName: map[string]any{ GeoLocFN: map[string]any{
"latitude": 14.1153, "latitude": ptr.To(lat),
"longitude": 120.962, "longitude": ptr.To(lon),
}, },
DispNameFieldName: "B123 Unit 1852 Prime Residences Tagaytay", DispNameFN: ptr.To(displayName),
} }
expectedData := map[string]any{ expectedData := map[string]any{
"MyAddress": map[string]any{ "MyAddress": fmt.Sprintf("%s,%s,%s,%s,%s,%s,%v,%v",
AddressFieldName: map[string]any{ displayName,
"city": "Tagaytay", street,
"countryOrRegion": "Philippines", city,
"postalCode": "4120", state,
"state": "Calabarzon", country,
"street": "Prime Residences CityLand 1852", postal,
}, lat,
CoordinatesFieldName: map[string]any{ lon),
"latitude": "14.1153",
"longitude": "120.962",
},
DisplayNameFieldName: "B123 Unit 1852 Prime Residences Tagaytay",
LocationURIFieldName: "https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032",
UniqueIDFieldName: "https://www.bingapis.com/api/v6/localbusinesses/YN8144x496766267081923032",
},
} }
origFs := models.NewFieldValueSet() origFs := models.NewFieldValueSet()
@ -515,6 +518,138 @@ func (suite *ListsUnitSuite) TestFieldValueSetable_Location() {
assert.Equal(t, expectedData, fsAdditionalData) assert.Equal(t, expectedData, fsAdditionalData)
} }
func (suite *ListsUnitSuite) TestConcatenateAddressFields() {
t := suite.T()
tests := []struct {
name string
addressFields map[string]any
expectedResult string
}{
{
name: "Valid Address",
addressFields: map[string]any{
DisplayNameKey: ptr.To("John Doe"),
AddressKey: map[string]any{
StreetKey: ptr.To("123 Main St"),
CityKey: ptr.To("Cityville"),
StateKey: ptr.To("State"),
CountryKey: ptr.To("Country"),
PostalCodeKey: ptr.To("12345"),
},
CoordinatesKey: map[string]any{
LatitudeKey: ptr.To(40.7128),
LongitudeKey: ptr.To(-74.0060),
},
},
expectedResult: "John Doe,123 Main St,Cityville,State,Country,12345,40.7128,-74.006",
},
{
name: "Empty Address Fields",
addressFields: map[string]any{
DisplayNameKey: ptr.To("John Doe"),
},
expectedResult: "John Doe",
},
{
name: "Empty Input",
addressFields: map[string]any{},
expectedResult: "",
},
}
for _, test := range tests {
suite.Run(test.name, func() {
result := concatenateAddressFields(test.addressFields)
assert.Equal(t, test.expectedResult, result, "address should match")
})
}
}
func (suite *ListsUnitSuite) TestHasAddressFields() {
t := suite.T()
tests := []struct {
name string
additionalData map[string]any
expectedFields map[string]any
expectedName string
expectedFound bool
}{
{
name: "Address Fields Found",
additionalData: map[string]any{
"person1": map[string]any{
AddressKey: map[string]any{
StreetKey: "123 Main St",
CityKey: "Cityville",
StateKey: "State",
CountryKey: "Country",
PostalCodeKey: "12345",
},
CoordinatesKey: map[string]any{
LatitudeKey: "40.7128",
LongitudeKey: "-74.0060",
},
DisplayNameKey: "John Doe",
LocationURIKey: "some loc",
UniqueIDKey: "some id",
},
},
expectedFields: map[string]any{
AddressKey: map[string]any{
StreetKey: "123 Main St",
CityKey: "Cityville",
StateKey: "State",
CountryKey: "Country",
PostalCodeKey: "12345",
},
CoordinatesKey: map[string]any{
LatitudeKey: "40.7128",
LongitudeKey: "-74.0060",
},
DisplayNameKey: "John Doe",
LocationURIKey: "some loc",
UniqueIDKey: "some id",
},
expectedName: "person1",
expectedFound: true,
},
{
name: "No Address Fields",
additionalData: map[string]any{
"person1": map[string]any{
"name": "John Doe",
"age": 30,
},
"person2": map[string]any{
"name": "Jane Doe",
"age": 25,
},
},
expectedFields: nil,
expectedName: "",
expectedFound: false,
},
{
name: "Empty Input",
additionalData: map[string]any{},
expectedFields: nil,
expectedName: "",
expectedFound: false,
},
}
for _, test := range tests {
suite.Run(test.name, func() {
fields, fieldName, found := hasAddressFields(test.additionalData)
require.Equal(t, test.expectedFound, found, "address fields identification should match")
assert.Equal(t, test.expectedName, fieldName, "address field name should match")
assert.Equal(t, test.expectedFields, fields, "address fields should match")
})
}
}
type ListsAPIIntgSuite struct { type ListsAPIIntgSuite struct {
tester.Suite tester.Suite
its intgTesterSetup its intgTesterSetup