@@ -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+
815852func 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.
885931func (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 )
0 commit comments