Preserve lambda Viper StringMap keys#857
Conversation
| out := make(map[string]struct{}) | ||
| for _, schemaField := range connectorSchema.Fields { | ||
| if schemaField.Variant == field.StringMapVariant { | ||
| out[strings.ToLower(schemaField.FieldName)] = struct{}{} |
There was a problem hiding this comment.
Why do we want to lower the field names here at all? WHat is consuming out that needs lowered names?
There was a problem hiding this comment.
Example config value:
map[string]any{
"user-custom-fields": map[string]any{
"customString10": "Custom String 10",
"departmentNav/name": "Department",
},
}
Non-Lambda CLI
Top-level field names are effectively Viper-managed and case-insensitive. The nested map keys are the customer’s payload and should stay as-is.
v.GetStringMapString("user-custom-fields")
// map[string]string{
// "customString10": "Custom String 10",
// "departmentNav/name": "Department",
// }
Lambda, Typed Connector
This is the SuccessFactors-style path. With PR #856, the typed generated config decodes the raw lambda payload directly, bypassing Viper.Set for the connector struct.
cfg.UserCustomFields
// map[string]any{
// "customString10": "Custom String 10",
// "departmentNav/name": "Department",
// }
PR #857 does not materially affect this path.
Lambda, Axiomatic / *viper.Viper
This is the future footgun path. Axiomatic receives *viper.Viper, so without PR #857 the nested keys would pass through Viper.Set as a map and become:
map[string]any{
"customstring10": "Custom String 10",
"departmentnav/name": "Department",
}
With PR #857, only schema-declared StringMapField values are converted before Viper.Set:
"user-custom-fields" => `{"customString10":"Custom String 10","departmentNav/name":"Department"}`
Then Axiomatic reads it normally:
v.GetStringMapString("user-custom-fields")
// map[string]string{
// "customString10": "Custom String 10",
// "departmentNav/name": "Department",
// }
we haven't used this field type yet for axiomatic but i think we want to set the precident as wanting case sensative - map keys are often external identifiers, not user-facing option names. They may be JSON paths, OData paths, SCIM attributes, API field names, headers, claim names, or vendor-specific IDs. Many of those are case-sensitive, or at least case-preserving.
0e61804 to
066aa0b
Compare
General PR Review: Preserve lambda Viper StringMap keysBlocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0 Review SummaryThis PR makes Security IssuesNone found. Correctness IssuesNone found. SuggestionsNone. |
Why
The typed-config fix preserves StringMap key case for generated connector configs, but lambda connectors that intentionally use *viper.Viper still receive the effective lambda config. Viper.Set recursively lowercases nested map[string]any keys, so dynamic StringMap fields can still lose case-sensitive keys.
What this changes
This makes the lambda effective config schema-aware for StringMapField values. Those payloads are stored as JSON strings before entering Viper, which keeps GetStringMap and GetStringMapString behavior while avoiding Viper's recursive map-key lowercasing. The PR also adds a regression test for the Viper path and keeps the existing typed-config test intact.
Depends on #856.
Validation