File size: 2,534 Bytes
d6f631f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | package subscription
import (
"errors"
"fmt"
"github.com/openmeterio/openmeter/pkg/models"
"github.com/openmeterio/openmeter/pkg/treex"
)
type fieldDescriptorMapping string
const (
PhaseDescriptor fieldDescriptorMapping = "phase"
)
// MapSubscriptionSpecValidationIssueField maps the FieldSelectors of a ValidationIssue
// from the structure of SubscriptionSpec to the structure of api.SubscriptionView
func MapSubscriptionSpecValidationIssueField(iss models.ValidationIssue) (models.ValidationIssue, error) {
// We'll do a tree walk and if we see annotated nodes we swap them with their mapping
var mappedField *models.FieldDescriptor
field := iss.Field()
if field == nil {
return iss, nil
}
err := field.Tree(func(t *models.FieldDescriptorTree) error {
err := t.DFS(func(n *treex.Node[*models.FieldDescriptor]) (bool, error) {
desc := n.Value()
// this will not happen for valid field descriptors but let's guard against nil anyways
if desc == nil {
return false, errors.New("field descriptor is nil")
}
return mapPhaseDescriptor(t, desc)
})
if err != nil {
return err
}
// We've finished the walk, we'll update the mapped field to be the tree root (as that might have been changed by the mapping)
mappedField = t.Root().Value().Clone()
return nil
})
if err != nil {
return iss, err
}
return iss.WithField(mappedField), nil
}
// mapPhaseDescriptor swaps a subtree to a single node with required FieldExpressions
func mapPhaseDescriptor(t *models.FieldDescriptorTree, current *models.FieldDescriptor) (bool, error) {
if attrs := current.GetAttributes(); attrs != nil {
if _, ok := attrs[PhaseDescriptor]; ok {
// Let's get the leafs from a tree starting at this node
leafs := make([]*models.FieldDescriptor, 0)
err := current.Tree(func(t *models.FieldDescriptorTree) error {
leafs = t.Leafs()
return nil
})
if err != nil {
return false, err
}
if len(leafs) != 2 {
return false, fmt.Errorf("phase selector segment %s has %d leafs, expected 2", current.String(), len(leafs))
}
phaseKey := leafs[1].String()
prunedAttrs := attrs.Clone()
delete(prunedAttrs, PhaseDescriptor)
mappedDesc := models.NewFieldSelector("phases").
WithExpression(models.NewFieldAttrValue("key", phaseKey)).
WithAttributes(prunedAttrs)
// Now we'll swap desc with mappedDesc. We don't need to walk any further as this is the only mapping we'll do.
return true, t.Swap(current, mappedDesc)
}
}
return false, nil
}
|