Skip to content

Commit d8157da

Browse files
feat: constraint group registry (#15741)
Co-authored-by: Stephen Lewis (Burrows) <[email protected]>
1 parent 4151101 commit d8157da

File tree

2 files changed

+90
-20
lines changed

2 files changed

+90
-20
lines changed

mmv1/api/resource.go

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,9 @@ type Resource struct {
367367
ImportPath string `yaml:"-"`
368368
SourceYamlFile string `yaml:"-"`
369369

370+
constraintGroupRegistry map[string]*[]string `yaml:"-"`
371+
constraintGroupsInitialized bool `yaml:"-"`
372+
370373
// ====================
371374
// TGC
372375
// ====================
@@ -812,10 +815,44 @@ func deduplicateSliceOfStrings(slice []string) []string {
812815
return result
813816
}
814817

818+
func (r *Resource) initializeConstraintGroups() {
819+
if r.constraintGroupsInitialized {
820+
return
821+
}
822+
r.constraintGroupRegistry = make(map[string]*[]string)
823+
824+
props := r.AllNestedProperties(google.Concat(r.RootProperties(), r.UserVirtualFields()))
825+
for _, prop := range props {
826+
prop.ConflictsGroup = r.attachConstraintGroup("conflicts", prop.Conflicts)
827+
prop.AtLeastOneOfGroup = r.attachConstraintGroup("at_least_one_of", prop.AtLeastOneOf)
828+
prop.ExactlyOneOfGroup = r.attachConstraintGroup("exactly_one_of", prop.ExactlyOneOf)
829+
prop.RequiredWithGroup = r.attachConstraintGroup("required_with", prop.RequiredWith)
830+
}
831+
r.constraintGroupsInitialized = true
832+
}
833+
834+
func (r *Resource) attachConstraintGroup(groupType string, source []string) *[]string {
835+
if len(source) == 0 {
836+
return nil
837+
}
838+
839+
sorted := slices.Clone(source)
840+
slices.Sort(sorted)
841+
key := fmt.Sprintf("%s|%s", groupType, strings.Join(sorted, ","))
842+
843+
if existing, ok := r.constraintGroupRegistry[key]; ok {
844+
return existing
845+
}
846+
847+
newGroup := slices.Clone(sorted)
848+
r.constraintGroupRegistry[key] = &newGroup
849+
return &newGroup
850+
}
851+
815852
func buildWriteOnlyField(name string, versionFieldName string, originalField *Type) *Type {
816853
description := fmt.Sprintf("%s Note: This property is write-only and will not be read from the API. For more info see [updating write-only attributes](/docs/providers/google/guides/using_write_only_attributes.html#updating-write-only-attributes)", originalField.Description)
817854
originalFieldLineage := originalField.TerraformLineage()
818-
fieldPathCurrentField := strings.ReplaceAll(originalFieldLineage, google.Underscore(originalField.Name), google.Underscore(name))
855+
newFieldLineage := strings.ReplaceAll(originalFieldLineage, google.Underscore(originalField.Name), google.Underscore(name))
819856
requiredWith := strings.ReplaceAll(originalFieldLineage, google.Underscore(originalField.Name), google.Underscore(versionFieldName))
820857

821858
apiName := originalField.ApiName
@@ -833,19 +870,28 @@ func buildWriteOnlyField(name string, versionFieldName string, originalField *Ty
833870
propertyWithRequiredWith([]string{requiredWith}),
834871
}
835872

836-
if originalField.Required {
873+
if originalField.Required || len(originalField.ExactlyOneOf) > 0 {
837874
originalField.Required = false
838-
exactlyOneOf := append(originalField.ExactlyOneOf, originalFieldLineage, fieldPathCurrentField)
839-
options = append(options, propertyWithExactlyOneOf(deduplicateSliceOfStrings(exactlyOneOf)))
840-
originalField.ExactlyOneOf = deduplicateSliceOfStrings(exactlyOneOf)
875+
if originalField.ExactlyOneOfGroup == nil {
876+
base := []string{originalFieldLineage, newFieldLineage}
877+
originalField.ExactlyOneOfGroup = &base
878+
} else {
879+
*originalField.ExactlyOneOfGroup = deduplicateSliceOfStrings(append(*originalField.ExactlyOneOfGroup, originalFieldLineage, newFieldLineage))
880+
}
881+
options = append(options, propertyWithExactlyOneOfPointer(originalField.ExactlyOneOfGroup))
841882
} else {
842-
conflicts := append(originalField.Conflicts, originalFieldLineage)
843-
options = append(options, propertyWithConflicts(deduplicateSliceOfStrings(conflicts)))
883+
if originalField.ConflictsGroup != nil {
884+
*originalField.ConflictsGroup = deduplicateSliceOfStrings(append(*originalField.ConflictsGroup, newFieldLineage))
885+
} else {
886+
originalField.Conflicts = deduplicateSliceOfStrings(append(originalField.Conflicts, newFieldLineage))
887+
}
888+
newConflicts := deduplicateSliceOfStrings(append([]string{originalFieldLineage}, originalField.Conflicts...))
889+
options = append(options, propertyWithConflicts(newConflicts))
844890
}
845891

846-
if len(originalField.AtLeastOneOf) > 0 {
847-
atLeastOneOf := append(originalField.AtLeastOneOf, originalFieldLineage, fieldPathCurrentField)
848-
options = append(options, propertyWithAtLeastOneOf(deduplicateSliceOfStrings(atLeastOneOf)))
892+
if originalField.AtLeastOneOfGroup != nil {
893+
*originalField.AtLeastOneOfGroup = deduplicateSliceOfStrings(append(*originalField.AtLeastOneOfGroup, originalFieldLineage, newFieldLineage))
894+
options = append(options, propertyWithAtLeastOneOfPointer(originalField.AtLeastOneOfGroup))
849895
}
850896

851897
return NewProperty(name, originalField.ApiName, options)
@@ -883,6 +929,10 @@ func (r *Resource) addWriteOnlyFields(props []*Type, propWithWoConfigured *Type)
883929
// AddExtraFields processes properties and adds supplementary fields based on property types.
884930
// It handles write-only properties, labels, and annotations.
885931
func (r *Resource) AddExtraFields(props []*Type, parent *Type) []*Type {
932+
if !r.constraintGroupsInitialized {
933+
r.initializeConstraintGroups()
934+
}
935+
886936
for _, p := range props {
887937
if p.WriteOnly && !strings.HasSuffix(p.Name, "Wo") {
888938
props = r.addWriteOnlyFields(props, p)

mmv1/api/type.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,12 @@ type Type struct {
136136
// A list of properties that are required to be set together.
137137
RequiredWith []string `yaml:"required_with,omitempty"`
138138

139+
// Shared constraint group pointers (populated post-unmarshal)
140+
ConflictsGroup *([]string) `yaml:"-"`
141+
AtLeastOneOfGroup *([]string) `yaml:"-"`
142+
ExactlyOneOfGroup *([]string) `yaml:"-"`
143+
RequiredWithGroup *([]string) `yaml:"-"`
144+
139145
// Can only be overridden - we should never set this ourselves.
140146
NewType string `yaml:"-"`
141147

@@ -703,7 +709,9 @@ func (t Type) Conflicting() []string {
703709
if t.ResourceMetadata == nil {
704710
return []string{}
705711
}
706-
712+
if t.ConflictsGroup != nil {
713+
return *t.ConflictsGroup
714+
}
707715
return t.Conflicts
708716
}
709717

@@ -723,7 +731,9 @@ func (t Type) AtLeastOneOfList() []string {
723731
if t.ResourceMetadata == nil {
724732
return []string{}
725733
}
726-
734+
if t.AtLeastOneOfGroup != nil {
735+
return *t.AtLeastOneOfGroup
736+
}
727737
return t.AtLeastOneOf
728738
}
729739

@@ -743,7 +753,9 @@ func (t Type) ExactlyOneOfList() []string {
743753
if t.ResourceMetadata == nil {
744754
return []string{}
745755
}
746-
756+
if t.ExactlyOneOfGroup != nil {
757+
return *t.ExactlyOneOfGroup
758+
}
747759
return t.ExactlyOneOf
748760
}
749761

@@ -762,7 +774,9 @@ func (t Type) RequiredWithList() []string {
762774
if t.ResourceMetadata == nil {
763775
return []string{}
764776
}
765-
777+
if t.RequiredWithGroup != nil {
778+
return *t.RequiredWithGroup
779+
}
766780
return t.RequiredWith
767781
}
768782

@@ -1227,12 +1241,6 @@ func propertyWithRequiredWith(requiredWith []string) func(*Type) {
12271241
}
12281242
}
12291243

1230-
func propertyWithExactlyOneOf(exactlyOneOf []string) func(*Type) {
1231-
return func(p *Type) {
1232-
p.ExactlyOneOf = exactlyOneOf
1233-
}
1234-
}
1235-
12361244
func propertyWithAtLeastOneOf(atLeastOneOf []string) func(*Type) {
12371245
return func(p *Type) {
12381246
p.AtLeastOneOf = atLeastOneOf
@@ -1245,6 +1253,18 @@ func propertyWithApiName(apiName string) func(*Type) {
12451253
}
12461254
}
12471255

1256+
func propertyWithExactlyOneOfPointer(ptr *[]string) func(*Type) {
1257+
return func(p *Type) {
1258+
p.ExactlyOneOfGroup = ptr
1259+
}
1260+
}
1261+
1262+
func propertyWithAtLeastOneOfPointer(ptr *[]string) func(*Type) {
1263+
return func(p *Type) {
1264+
p.AtLeastOneOfGroup = ptr
1265+
}
1266+
}
1267+
12481268
func (t *Type) validateLabelsField() {
12491269
productName := t.ResourceMetadata.ProductMetadata.Name
12501270
resourceName := t.ResourceMetadata.Name

0 commit comments

Comments
 (0)