diff --git a/src/pkg/services/m365/api/consts.go b/src/pkg/services/m365/api/consts.go index b5342f8c80..e9edb623ae 100644 --- a/src/pkg/services/m365/api/consts.go +++ b/src/pkg/services/m365/api/consts.go @@ -62,6 +62,10 @@ const ( PersonEmailKey = "Email" + MetadataLabelKey = "Label" + MetadataTermGUIDKey = "TermGuid" + MetadataWssIDKey = "WssId" + LinkTitleFieldNamePart = "LinkTitle" ChildCountFieldNamePart = "ChildCount" LookupIDFieldNamePart = "LookupId" diff --git a/src/pkg/services/m365/api/lists.go b/src/pkg/services/m365/api/lists.go index 4a5e76f91d..5c5ec2d1b1 100644 --- a/src/pkg/services/m365/api/lists.go +++ b/src/pkg/services/m365/api/lists.go @@ -511,6 +511,10 @@ func retrieveFieldData(orig models.FieldValueSetable, columnNames map[string]*co additionalData[fieldName] = concatenatedHyperlink } + if metadataField, fieldName, ok := hasMetadataFields(additionalData); ok { + additionalData[fieldName] = concatenateMetadataFields(metadataField) + } + fields.SetAdditionalData(additionalData) return fields @@ -647,6 +651,44 @@ func hasHyperLinkFields(additionalData map[string]any) (map[string]any, string, return nil, "", false } +func hasMetadataFields(additionalData map[string]any) ([]map[string]any, string, bool) { + for fieldName, value := range additionalData { + switch valType := reflect.TypeOf(value).Kind(); valType { + case reflect.Map: + metadataFields, areMetadataFields := getMetadataFields(value) + if areMetadataFields { + return []map[string]any{metadataFields}, fieldName, true + } + + case reflect.Slice: + mmdfs := make([]map[string]any, 0) + + multiMetadataFields, ok := value.([]any) + if !ok { + continue + } + + for _, mdfs := range multiMetadataFields { + metadataFields, areMetadataFields := getMetadataFields(mdfs) + if areMetadataFields { + mmdfs = append(mmdfs, metadataFields) + } + } + + if len(mmdfs) > 0 { + return mmdfs, fieldName, true + } + } + } + + return nil, "", false +} + +func getMetadataFields(metadataFieldvalue any) (map[string]any, bool) { + nestedFields, ok := metadataFieldvalue.(map[string]any) + return nestedFields, ok && keys.HasKeys(nestedFields, MetadataLabelKey, MetadataTermGUIDKey, MetadataWssIDKey) +} + func concatenateAddressFields(addressFields map[string]any) string { parts := make([]string, 0) @@ -692,6 +734,23 @@ func concatenateHyperLinkFields(hyperlinkFields map[string]any) string { return "" } +func concatenateMetadataFields(metadataFieldsArr []map[string]any) string { + labels := make([]string, 0) + + for _, md := range metadataFieldsArr { + mdVal, ok := md[MetadataLabelKey].(*string) + if ok { + labels = append(labels, ptr.Val(mdVal)) + } + } + + if len(labels) > 0 { + return strings.Join(labels, ",") + } + + 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 d0d08f396a..1417d84153 100644 --- a/src/pkg/services/m365/api/lists_test.go +++ b/src/pkg/services/m365/api/lists_test.go @@ -941,6 +941,161 @@ func (suite *ListsUnitSuite) TestSetAdditionalDataByColumnNames() { } } +func (suite *ListsUnitSuite) TestHasMetadataFields() { + t := suite.T() + + tests := []struct { + name string + additionalData map[string]any + expectedFields []map[string]any + expectedFieldName string + hasMetadataFields bool + }{ + { + name: "Single metadata fields, has all keys", + additionalData: map[string]any{ + "MdCol": map[string]any{ + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + }, + expectedFields: []map[string]any{ + { + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + }, + expectedFieldName: "MdCol", + hasMetadataFields: true, + }, + { + name: "Multiple metadata fields, has all keys", + additionalData: map[string]any{ + "MdCol": []any{ + map[string]any{ + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + map[string]any{ + MetadataLabelKey: ptr.To("Marketing"), + MetadataTermGUIDKey: ptr.To("312347ce-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(2), + }, + }, + }, + expectedFields: []map[string]any{ + { + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + { + MetadataLabelKey: ptr.To("Marketing"), + MetadataTermGUIDKey: ptr.To("312347ce-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(2), + }, + }, + expectedFieldName: "MdCol", + hasMetadataFields: true, + }, + { + name: "Single metadata fields, missing few keys", + additionalData: map[string]any{ + "MdCol": map[string]any{ + MetadataLabelKey: ptr.To("Engineering"), + }, + }, + expectedFields: nil, + expectedFieldName: "", + hasMetadataFields: false, + }, + { + name: "Multiple metadata fields, missing few keys", + additionalData: map[string]any{ + "MdCol": []any{ + map[string]any{ + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + map[string]any{ + MetadataLabelKey: ptr.To("Marketing"), + }, + }, + }, + expectedFields: []map[string]any{ + { + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + }, + expectedFieldName: "MdCol", + hasMetadataFields: true, + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + nestedFields, fName, isMetadata := hasMetadataFields(test.additionalData) + assert.Equal(t, test.expectedFields, nestedFields) + assert.Equal(t, test.expectedFieldName, fName) + assert.Equal(t, test.hasMetadataFields, isMetadata) + }) + } +} + +func (suite *ListsUnitSuite) TestConcatenateMetadataFields() { + t := suite.T() + + tests := []struct { + name string + metadataFields []map[string]any + expectedFieldName string + expectedResult string + hasMetadataFields bool + columnNames map[string]any + }{ + { + name: "Single metadata fields", + metadataFields: []map[string]any{ + { + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + }, + expectedResult: "Engineering", + }, + { + name: "Multiple metadata fields", + metadataFields: []map[string]any{ + { + MetadataLabelKey: ptr.To("Engineering"), + MetadataTermGUIDKey: ptr.To("6b5d3ce9-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(4), + }, + { + MetadataLabelKey: ptr.To("Marketing"), + MetadataTermGUIDKey: ptr.To("312347ce-3043-499f-8be6-e92fb57bed96"), + MetadataWssIDKey: ptr.To(2), + }, + }, + expectedResult: "Engineering,Marketing", + }, + } + + for _, test := range tests { + suite.Run(test.name, func() { + res := concatenateMetadataFields(test.metadataFields) + assert.Equal(t, test.expectedResult, res) + }) + } +} + func (suite *ListsUnitSuite) TestCheckFields() { t := suite.T()