sets list item fields based on columns created (#4969)
sets list item fields based on columns created while list creation #### 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] 🧹 Tech Debt/Cleanup #### 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:
parent
35ac37313d
commit
8067c72904
@ -25,6 +25,7 @@ const (
|
|||||||
AuthorLookupIDColumnName = "AuthorLookupId"
|
AuthorLookupIDColumnName = "AuthorLookupId"
|
||||||
EditorLookupIDColumnName = "EditorLookupId"
|
EditorLookupIDColumnName = "EditorLookupId"
|
||||||
AppAuthorLookupIDColumnName = "AppAuthorLookupId"
|
AppAuthorLookupIDColumnName = "AppAuthorLookupId"
|
||||||
|
TitleColumnName = "Title"
|
||||||
|
|
||||||
ContentTypeColumnDisplayName = "Content Type"
|
ContentTypeColumnDisplayName = "Content Type"
|
||||||
|
|
||||||
@ -56,38 +57,12 @@ const (
|
|||||||
AccessRequestsListTemplate = "accessRequest"
|
AccessRequestsListTemplate = "accessRequest"
|
||||||
)
|
)
|
||||||
|
|
||||||
var addressFieldNames = []string{
|
|
||||||
AddressFieldName,
|
|
||||||
CoordinatesFieldName,
|
|
||||||
DisplayNameFieldName,
|
|
||||||
LocationURIFieldName,
|
|
||||||
UniqueIDFieldName,
|
|
||||||
}
|
|
||||||
|
|
||||||
var readOnlyAddressFieldNames = []string{
|
|
||||||
CountryOrRegionFieldName,
|
|
||||||
StateFieldName,
|
|
||||||
CityFieldName,
|
|
||||||
PostalCodeFieldName,
|
|
||||||
StreetFieldName,
|
|
||||||
GeoLocFieldName,
|
|
||||||
DispNameFieldName,
|
|
||||||
}
|
|
||||||
|
|
||||||
var legacyColumns = keys.Set{
|
var legacyColumns = keys.Set{
|
||||||
AttachmentsColumnName: {},
|
AttachmentsColumnName: {},
|
||||||
EditColumnName: {},
|
EditColumnName: {},
|
||||||
ContentTypeColumnDisplayName: {},
|
ContentTypeColumnDisplayName: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
var readOnlyFieldNames = keys.Set{
|
|
||||||
AttachmentsColumnName: {},
|
|
||||||
EditColumnName: {},
|
|
||||||
ContentTypeColumnName: {},
|
|
||||||
CreatedColumnName: {},
|
|
||||||
ModifiedColumnName: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
var SkipListTemplates = keys.Set{
|
var SkipListTemplates = keys.Set{
|
||||||
WebTemplateExtensionsListTemplate: {},
|
WebTemplateExtensionsListTemplate: {},
|
||||||
DocumentLibraryListTemplate: {},
|
DocumentLibraryListTemplate: {},
|
||||||
@ -266,7 +241,7 @@ func (c Lists) PostList(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this ensure all columns, contentTypes are set to the newList
|
// this ensure all columns, contentTypes are set to the newList
|
||||||
newList := ToListable(oldList, newListName)
|
newList, columnNames := ToListable(oldList, newListName)
|
||||||
|
|
||||||
if newList.GetList() != nil &&
|
if newList.GetList() != nil &&
|
||||||
SkipListTemplates.HasKey(ptr.Val(newList.GetList().GetTemplate())) {
|
SkipListTemplates.HasKey(ptr.Val(newList.GetList().GetTemplate())) {
|
||||||
@ -287,7 +262,7 @@ func (c Lists) PostList(
|
|||||||
listItems := make([]models.ListItemable, 0)
|
listItems := make([]models.ListItemable, 0)
|
||||||
|
|
||||||
for _, itm := range oldList.GetItems() {
|
for _, itm := range oldList.GetItems() {
|
||||||
temp := CloneListItem(itm)
|
temp := CloneListItem(itm, columnNames)
|
||||||
listItems = append(listItems, temp)
|
listItems = append(listItems, temp)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +335,7 @@ func BytesToListable(bytes []byte) (models.Listable, error) {
|
|||||||
// not attached in this method.
|
// not attached in this method.
|
||||||
// ListItems are not included in creation of new list, and have to be restored
|
// ListItems are not included in creation of new list, and have to be restored
|
||||||
// in separate call.
|
// in separate call.
|
||||||
func ToListable(orig models.Listable, displayName string) models.Listable {
|
func ToListable(orig models.Listable, displayName string) (models.Listable, map[string]any) {
|
||||||
newList := models.NewList()
|
newList := models.NewList()
|
||||||
|
|
||||||
newList.SetContentTypes(orig.GetContentTypes())
|
newList.SetContentTypes(orig.GetContentTypes())
|
||||||
@ -377,6 +352,7 @@ func ToListable(orig models.Listable, displayName string) models.Listable {
|
|||||||
newList.SetParentReference(orig.GetParentReference())
|
newList.SetParentReference(orig.GetParentReference())
|
||||||
|
|
||||||
columns := make([]models.ColumnDefinitionable, 0)
|
columns := make([]models.ColumnDefinitionable, 0)
|
||||||
|
columnNames := map[string]any{TitleColumnName: nil}
|
||||||
|
|
||||||
for _, cd := range orig.GetColumns() {
|
for _, cd := range orig.GetColumns() {
|
||||||
var (
|
var (
|
||||||
@ -394,16 +370,17 @@ func ToListable(orig models.Listable, displayName string) models.Listable {
|
|||||||
|
|
||||||
// Skips columns that cannot be uploaded for models.ColumnDefinitionable:
|
// Skips columns that cannot be uploaded for models.ColumnDefinitionable:
|
||||||
// - ReadOnly, Title, or Legacy columns: Attachments, Edit, or Content Type
|
// - ReadOnly, Title, or Legacy columns: Attachments, Edit, or Content Type
|
||||||
if readOnly || displayName == "Title" || legacyColumns.HasKey(displayName) {
|
if readOnly || displayName == TitleColumnName || legacyColumns.HasKey(displayName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
columns = append(columns, cloneColumnDefinitionable(cd))
|
columns = append(columns, cloneColumnDefinitionable(cd))
|
||||||
|
columnNames[ptr.Val(cd.GetName())] = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newList.SetColumns(columns)
|
newList.SetColumns(columns)
|
||||||
|
|
||||||
return newList
|
return newList, columnNames
|
||||||
}
|
}
|
||||||
|
|
||||||
// cloneColumnDefinitionable utility function for encapsulating models.ColumnDefinitionable data
|
// cloneColumnDefinitionable utility function for encapsulating models.ColumnDefinitionable data
|
||||||
@ -486,11 +463,11 @@ func setColumnType(newColumn *models.ColumnDefinition, orig models.ColumnDefinit
|
|||||||
// CloneListItem creates a new `SharePoint.ListItem` and stores the original item's
|
// CloneListItem creates a new `SharePoint.ListItem` and stores the original item's
|
||||||
// M365 data into it set fields.
|
// M365 data into it set fields.
|
||||||
// - https://learn.microsoft.com/en-us/graph/api/resources/listitem?view=graph-rest-1.0
|
// - https://learn.microsoft.com/en-us/graph/api/resources/listitem?view=graph-rest-1.0
|
||||||
func CloneListItem(orig models.ListItemable) models.ListItemable {
|
func CloneListItem(orig models.ListItemable, columnNames map[string]any) models.ListItemable {
|
||||||
newItem := models.NewListItem()
|
newItem := models.NewListItem()
|
||||||
|
|
||||||
// list item data
|
// list item data
|
||||||
newFieldData := retrieveFieldData(orig.GetFields())
|
newFieldData := retrieveFieldData(orig.GetFields(), columnNames)
|
||||||
newItem.SetFields(newFieldData)
|
newItem.SetFields(newFieldData)
|
||||||
|
|
||||||
// list item attributes
|
// list item attributes
|
||||||
@ -523,18 +500,19 @@ func CloneListItem(orig models.ListItemable) models.ListItemable {
|
|||||||
// additionalData map
|
// additionalData map
|
||||||
// Further documentation on FieldValueSets:
|
// Further documentation on FieldValueSets:
|
||||||
// - https://learn.microsoft.com/en-us/graph/api/resources/fieldvalueset?view=graph-rest-1.0
|
// - https://learn.microsoft.com/en-us/graph/api/resources/fieldvalueset?view=graph-rest-1.0
|
||||||
func retrieveFieldData(orig models.FieldValueSetable) models.FieldValueSetable {
|
func retrieveFieldData(orig models.FieldValueSetable, columnNames map[string]any) models.FieldValueSetable {
|
||||||
fields := models.NewFieldValueSet()
|
fields := models.NewFieldValueSet()
|
||||||
additionalData := filterAdditionalData(orig)
|
|
||||||
|
|
||||||
retainPrimaryAddressField(additionalData)
|
|
||||||
|
|
||||||
|
additionalData := setAdditionalDataByColumnNames(orig, columnNames)
|
||||||
fields.SetAdditionalData(additionalData)
|
fields.SetAdditionalData(additionalData)
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterAdditionalData(orig models.FieldValueSetable) map[string]any {
|
func setAdditionalDataByColumnNames(
|
||||||
|
orig models.FieldValueSetable,
|
||||||
|
columnNames map[string]any,
|
||||||
|
) map[string]any {
|
||||||
if orig == nil {
|
if orig == nil {
|
||||||
return make(map[string]any)
|
return make(map[string]any)
|
||||||
}
|
}
|
||||||
@ -542,55 +520,15 @@ func filterAdditionalData(orig models.FieldValueSetable) map[string]any {
|
|||||||
fieldData := orig.GetAdditionalData()
|
fieldData := orig.GetAdditionalData()
|
||||||
filteredData := make(map[string]any)
|
filteredData := make(map[string]any)
|
||||||
|
|
||||||
for key, value := range fieldData {
|
for colName := range columnNames {
|
||||||
if shouldFilterField(key, value) {
|
if _, ok := fieldData[colName]; ok {
|
||||||
continue
|
filteredData[colName] = fieldData[colName]
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredData[key] = value
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredData
|
return filteredData
|
||||||
}
|
}
|
||||||
|
|
||||||
func shouldFilterField(key string, value any) bool {
|
|
||||||
return readOnlyFieldNames.HasKey(key) ||
|
|
||||||
strings.HasPrefix(key, ReadOnlyOrHiddenFieldNamePrefix) ||
|
|
||||||
strings.HasPrefix(key, DescoratorFieldNamePrefix) ||
|
|
||||||
strings.Contains(key, LinkTitleFieldNamePart) ||
|
|
||||||
strings.Contains(key, ChildCountFieldNamePart) ||
|
|
||||||
strings.Contains(key, LookupIDFieldNamePart)
|
|
||||||
}
|
|
||||||
|
|
||||||
func retainPrimaryAddressField(additionalData map[string]any) {
|
|
||||||
if !hasAddressFields(additionalData) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, k := range readOnlyAddressFieldNames {
|
|
||||||
delete(additionalData, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasAddressFields(additionalData map[string]any) bool {
|
|
||||||
if !keys.HasKeys(additionalData, readOnlyAddressFieldNames...) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, value := range additionalData {
|
|
||||||
nestedFields, ok := value.(map[string]any)
|
|
||||||
if !ok || keys.HasKeys(nestedFields, GeoLocFieldName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if keys.HasKeys(nestedFields, addressFieldNames...) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Lists) getListItemFields(
|
func (c Lists) getListItemFields(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
siteID, listID, itemID string,
|
siteID, listID, itemID string,
|
||||||
|
|||||||
@ -328,9 +328,10 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
roCd.SetReadOnly(ptr.To(true))
|
roCd.SetReadOnly(ptr.To(true))
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
getList func() *models.List
|
getList func() *models.List
|
||||||
length int
|
length int
|
||||||
|
expectedColNames map[string]any
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "all legacy columns",
|
name: "all legacy columns",
|
||||||
@ -343,7 +344,8 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
})
|
})
|
||||||
return lst
|
return lst
|
||||||
},
|
},
|
||||||
length: 0,
|
length: 0,
|
||||||
|
expectedColNames: map[string]any{TitleColumnName: nil},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "title and legacy columns",
|
name: "title and legacy columns",
|
||||||
@ -357,7 +359,8 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
})
|
})
|
||||||
return lst
|
return lst
|
||||||
},
|
},
|
||||||
length: 0,
|
length: 0,
|
||||||
|
expectedColNames: map[string]any{TitleColumnName: nil},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "readonly and legacy columns",
|
name: "readonly and legacy columns",
|
||||||
@ -371,7 +374,8 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
})
|
})
|
||||||
return lst
|
return lst
|
||||||
},
|
},
|
||||||
length: 0,
|
length: 0,
|
||||||
|
expectedColNames: map[string]any{TitleColumnName: nil},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "legacy and a text column",
|
name: "legacy and a text column",
|
||||||
@ -386,6 +390,10 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
return lst
|
return lst
|
||||||
},
|
},
|
||||||
length: 1,
|
length: 1,
|
||||||
|
expectedColNames: map[string]any{
|
||||||
|
TitleColumnName: nil,
|
||||||
|
textColumnName: nil,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,8 +401,9 @@ func (suite *ListsUnitSuite) TestColumnDefinitionable_LegacyColumns() {
|
|||||||
suite.Run(test.name, func() {
|
suite.Run(test.name, func() {
|
||||||
t := suite.T()
|
t := suite.T()
|
||||||
|
|
||||||
clonedList := ToListable(test.getList(), listName)
|
clonedList, colNames := ToListable(test.getList(), listName)
|
||||||
require.NotEmpty(t, clonedList)
|
require.NotEmpty(t, clonedList)
|
||||||
|
assert.Equal(t, test.expectedColNames, colNames)
|
||||||
|
|
||||||
cols := clonedList.GetColumns()
|
cols := clonedList.GetColumns()
|
||||||
assert.Len(t, cols, test.length)
|
assert.Len(t, cols, test.length)
|
||||||
@ -422,7 +431,9 @@ func (suite *ListsUnitSuite) TestFieldValueSetable() {
|
|||||||
origFs := models.NewFieldValueSet()
|
origFs := models.NewFieldValueSet()
|
||||||
origFs.SetAdditionalData(additionalData)
|
origFs.SetAdditionalData(additionalData)
|
||||||
|
|
||||||
fs := retrieveFieldData(origFs)
|
colNames := map[string]any{}
|
||||||
|
|
||||||
|
fs := retrieveFieldData(origFs, colNames)
|
||||||
fsAdditionalData := fs.GetAdditionalData()
|
fsAdditionalData := fs.GetAdditionalData()
|
||||||
assert.Empty(t, fsAdditionalData)
|
assert.Empty(t, fsAdditionalData)
|
||||||
|
|
||||||
@ -430,7 +441,9 @@ func (suite *ListsUnitSuite) TestFieldValueSetable() {
|
|||||||
origFs = models.NewFieldValueSet()
|
origFs = models.NewFieldValueSet()
|
||||||
origFs.SetAdditionalData(additionalData)
|
origFs.SetAdditionalData(additionalData)
|
||||||
|
|
||||||
fs = retrieveFieldData(origFs)
|
colNames["itemName"] = struct{}{}
|
||||||
|
|
||||||
|
fs = retrieveFieldData(origFs, colNames)
|
||||||
fsAdditionalData = fs.GetAdditionalData()
|
fsAdditionalData = fs.GetAdditionalData()
|
||||||
assert.NotEmpty(t, fsAdditionalData)
|
assert.NotEmpty(t, fsAdditionalData)
|
||||||
|
|
||||||
@ -493,7 +506,11 @@ func (suite *ListsUnitSuite) TestFieldValueSetable_Location() {
|
|||||||
origFs := models.NewFieldValueSet()
|
origFs := models.NewFieldValueSet()
|
||||||
origFs.SetAdditionalData(additionalData)
|
origFs.SetAdditionalData(additionalData)
|
||||||
|
|
||||||
fs := retrieveFieldData(origFs)
|
colNames := map[string]any{
|
||||||
|
"MyAddress": nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := retrieveFieldData(origFs, colNames)
|
||||||
fsAdditionalData := fs.GetAdditionalData()
|
fsAdditionalData := fs.GetAdditionalData()
|
||||||
assert.Equal(t, expectedData, fsAdditionalData)
|
assert.Equal(t, expectedData, fsAdditionalData)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user