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:
parent
b459d27cc7
commit
06afd53660
@ -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,19 +31,29 @@ 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"
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
|
||||||
CountryOrRegionFieldName = "CountryOrRegion"
|
|
||||||
StateFieldName = "State"
|
|
||||||
CityFieldName = "City"
|
|
||||||
PostalCodeFieldName = "PostalCode"
|
|
||||||
StreetFieldName = "Street"
|
|
||||||
GeoLocFieldName = "GeoLoc"
|
|
||||||
DispNameFieldName = "DispName"
|
|
||||||
LinkTitleFieldNamePart = "LinkTitle"
|
LinkTitleFieldNamePart = "LinkTitle"
|
||||||
ChildCountFieldNamePart = "ChildCount"
|
ChildCountFieldNamePart = "ChildCount"
|
||||||
LookupIDFieldNamePart = "LookupId"
|
LookupIDFieldNamePart = "LookupId"
|
||||||
@ -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,
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user