Skip to content

Commit

Permalink
handles single & multiple values for metadata columns (#5121)
Browse files Browse the repository at this point in the history
handles single & multiple values for metadata columns

Similar to `Hyperlink` and `Column` columns, `Metadata` column too is unrecognizable from GRAPH API response. Hence identifying from the field column names.

`Metadata` fields are like tags. A `Metadata` fields can be configured to hold multiple values/tags

**Original List with `Metadata` column (Department) with single value/tag**:
![Metadata-List](https://github.com/alcionai/corso/assets/48874082/0b913a2a-46d5-4d9c-83f9-69a5236b1024)

**Restored List with `Metadata` column with single value/tag**:
![Restored-Metadata-List](https://github.com/alcionai/corso/assets/48874082/9420012b-345c-4fac-90c3-c0d421b2edfb)

**Original List with `Metadata` column (Department) with multiple value/tag**:
![Metadata-List-Multi](https://github.com/alcionai/corso/assets/48874082/054ef4a1-c46e-48ba-b410-a95b540cde33)

**Restored List with `Metadata` column with multiple value/tag**:
![Restored-Multi-Metadata-List](https://github.com/alcionai/corso/assets/48874082/ef6c904b-e431-4a85-9ef2-f08bcf8e21e4)


#### Does this PR need a docs update or release note?

- [ ] ✅ Yes, it's included
- [ ] 🕐 Yes, but in a later PR
- [x] ⛔ No

#### Type of change

<!--- Please check the type of change your PR introduces: --->
- [x] 🌻 Feature
- [ ] 🐛 Bugfix
- [ ] 🗺️ Documentation
- [ ] 🤖 Supportability/Tests
- [ ] 💻 CI/Deployment
- [ ] 🧹 Tech Debt/Cleanup

#### Issue(s)
#5084 
#5108 

#### Test Plan

<!-- How will this be tested prior to merging.-->
- [x] 💪 Manual
- [x] ⚡ Unit test
- [x] 💚 E2E
  • Loading branch information
HiteshRepo committed Jan 30, 2024
1 parent 08d4803 commit 576c9f6
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/pkg/services/m365/api/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ const (

PersonEmailKey = "Email"

MetadataLabelKey = "Label"
MetadataTermGUIDKey = "TermGuid"
MetadataWssIDKey = "WssId"

LinkTitleFieldNamePart = "LinkTitle"
ChildCountFieldNamePart = "ChildCount"
LookupIDFieldNamePart = "LookupId"
Expand Down
59 changes: 59 additions & 0 deletions src/pkg/services/m365/api/lists.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
155 changes: 155 additions & 0 deletions src/pkg/services/m365/api/lists_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down

0 comments on commit 576c9f6

Please sign in to comment.