Skip to content

Commit 88ff428

Browse files
authored
Implement merging overrides when grouping configs (#288)
1 parent d152f48 commit 88ff428

File tree

1 file changed

+57
-0
lines changed

1 file changed

+57
-0
lines changed

cmd/lekko/feature.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ func configGroup() *cobra.Command {
441441
// which we use to determine if a config is an enum config
442442
// Keep map of (enum) config names to translated enum name and values
443443
// We'll build up the values lookup map in a later stage
444+
// TODO: maybe cleanup into a proper type
444445
enumLookupMap := make(map[string]struct {
445446
name string
446447
values map[string]string
@@ -592,6 +593,62 @@ func configGroup() *cobra.Command {
592593
defaultValue.Set(mt.Descriptor().Fields().ByName(protoreflect.Name(protoFieldNames[i])), protoreflect.ValueOf(orig.Value))
593594
}
594595
newF.Value = defaultValue
596+
// TODO: Should probably extract this/enum handling logic out for better code org...
597+
// Handle overrides using the following logic:
598+
// 1. Flatten out all overrides
599+
// 2. Scan rules to check if any are identical to a previous one, note
600+
// 3. Merge overrides with the same rules "upwards"
601+
// 4. Write into overall overrides
602+
overrides := make([]*feature.Override, 0)
603+
ruleFirsts := make(map[string]*feature.Override) // Map of rulestrings to pointers of the first override that has them
604+
for i, cn := range configNames {
605+
orig := compiledMap[cn]
606+
enumLookup, isEnum := enumLookupMap[cn]
607+
for _, origOverride := range orig.Overrides {
608+
var origValProto protoreflect.Value
609+
if isEnum {
610+
if origVal, ok := origOverride.Value.(string); ok {
611+
enumDescriptor := mt.Descriptor().Enums().ByName(protoreflect.Name(enumLookup.name))
612+
if enumDescriptor == nil {
613+
return errors.Errorf("missing enum descriptor for %s", enumLookup.name)
614+
}
615+
valueDescriptor := enumDescriptor.Values().ByName(protoreflect.Name(enumLookup.values[origVal]))
616+
if valueDescriptor == nil {
617+
return errors.Errorf("missing enum value for %s", enumLookup.values[origVal])
618+
}
619+
origValProto = protoreflect.ValueOf(valueDescriptor.Number())
620+
} else {
621+
return errors.New("unexpected non-string original value")
622+
}
623+
} else {
624+
origValProto = protoreflect.ValueOf(origOverride.Value)
625+
}
626+
protoField := mt.Descriptor().Fields().ByName(protoreflect.Name(protoFieldNames[i]))
627+
// Check if an earlier override had this rule, if so merge into it
628+
ruleFirst, ruleSeen := ruleFirsts[origOverride.Rule]
629+
if ruleSeen {
630+
if value, ok := ruleFirst.Value.(protoreflect.Message); ok {
631+
value.Set(protoField, origValProto)
632+
} else {
633+
return errors.Errorf("unexpected type while merging overrides %T", ruleFirst.Value)
634+
}
635+
} else {
636+
value := mt.New()
637+
value.Set(protoField, origValProto)
638+
override := &feature.Override{
639+
Rule: origOverride.Rule,
640+
RuleASTV3: origOverride.RuleASTV3,
641+
Value: value,
642+
}
643+
overrides = append(overrides, override)
644+
if _, ok := ruleFirsts[origOverride.Rule]; !ok {
645+
ruleFirsts[origOverride.Rule] = override
646+
}
647+
ruleFirsts[override.Rule] = override
648+
}
649+
}
650+
}
651+
newF.Overrides = overrides
595652
sf, err := r.Parse(ctx, ns, outName, registry)
596653
if err != nil {
597654
return errors.Wrap(err, "parse generated config")

0 commit comments

Comments
 (0)