diff --git a/pkg/generate/code/set_resource.go b/pkg/generate/code/set_resource.go index 707c88ebc..6c73062bd 100644 --- a/pkg/generate/code/set_resource.go +++ b/pkg/generate/code/set_resource.go @@ -1805,18 +1805,37 @@ func SetResourceForStruct( indent := strings.Repeat("\t", indentLevel) sourceShape := sourceShapeRef.Shape targetShape := targetShapeRef.Shape + operation := operationForType(r, op) var sourceMemberShapeRef *awssdkmodel.ShapeRef var sourceAdaptedVarName, qualifiedTargetVar string for _, targetMemberName := range targetShape.MemberNames() { + targetMemberFieldName := names.New(targetMemberName).Camel + if operation != nil { + fullPath := targetFieldPath + "." + targetMemberName + renamed := cfg.GetResourceFieldName(r.Names.Original, operation.ExportedName, fullPath) + if renamed != fullPath { + targetMemberFieldName = names.New(renamed).Camel + } + } + + // Look up the source (SDK) field name for this renamed CRD field + sourceMemberName := targetMemberName + if operation != nil { + sourceMemberName = getSourceFieldNameForRename( + cfg, r.Names.Original, operation.ExportedName, + targetFieldPath, targetMemberFieldName, + ) + } // To check if the field member has `ignore` set to `true`. // This condition currently applies only for members of a field whose shape is `structure`. var setCfg *ackgenconfig.SetFieldConfig f, ok := r.Fields[targetFieldPath] if ok { - mf, ok := f.MemberFields[names.New(targetMemberName).Camel] + mf, ok := f.MemberFields[targetMemberFieldName] if ok { + targetMemberFieldName = mf.Names.Camel setCfg = mf.GetSetterConfig(op) if setCfg != nil && setCfg.IgnoreResourceSetter() { continue @@ -1827,7 +1846,8 @@ func SetResourceForStruct( } } - sourceMemberShapeRef = sourceShape.MemberRefs[targetMemberName] + sourceMemberName = resolveSourceMemberName(sourceShape, sourceMemberName) + sourceMemberShapeRef = sourceShape.MemberRefs[sourceMemberName] if sourceMemberShapeRef == nil { continue } @@ -1836,7 +1856,7 @@ func SetResourceForStruct( } // Upstream logic iterates over sourceShape members and therefore uses // the sourceShape's index; continue using sourceShape's index here for consistency. - sourceMemberIndex, err := GetMemberIndex(sourceShape, targetMemberName) + sourceMemberIndex, err := GetMemberIndex(sourceShape, sourceMemberName) if err != nil { return "", fmt.Errorf( "resource %q, field %q: could not determine source shape index: %w", @@ -1847,8 +1867,8 @@ func SetResourceForStruct( targetMemberShapeRef := targetShape.MemberRefs[targetMemberName] indexedVarName := fmt.Sprintf("%sf%d", targetVarName, sourceMemberIndex) sourceMemberShape := sourceMemberShapeRef.Shape - targetMemberCleanNames := names.New(targetMemberName) - sourceAdaptedVarName = sourceVarName + "." + targetMemberName + targetMemberCleanNames := names.New(targetMemberFieldName) + sourceAdaptedVarName = sourceVarName + "." + sourceMemberName // Enum types are just strings at the end of the day // so we want to check if they are empty before deciding @@ -1876,7 +1896,7 @@ func SetResourceForStruct( // if lists are made of strings, or maps are made of string-to-string, we want to leverage // the aws-sdk-go-v2 provided function to convert from pointer to non-pointer collection case "list", "structure", "map", "union": - adaption := setResourceAdaptPrimitiveCollection(sourceMemberShape, qualifiedTargetVar, sourceAdaptedVarName, indent, r.IsSecretField(targetMemberName)) + adaption := setResourceAdaptPrimitiveCollection(sourceMemberShape, qualifiedTargetVar, sourceAdaptedVarName, indent, r.IsSecretField(targetFieldPath+"."+targetMemberFieldName)) out += adaption if adaption != "" { break @@ -2461,6 +2481,7 @@ func setResourceForUnion( var sourceMemberShapeRef *awssdkmodel.ShapeRef var sourceAdaptedVarName, qualifiedTargetVar string + operation := operationForType(r, op) sdkGoType := sourceShape.GoTypeWithPkgName() sdkGoType = model.ReplacePkgName(sdkGoType, r.SDKAPIPackageName(), "svcsdktypes", true) @@ -2472,11 +2493,29 @@ func setResourceForUnion( out += fmt.Sprintf("%sswitch %s.(type) {\n", indent, sourceVarName) for _, targetMemberName := range targetShape.MemberNames() { + targetMemberFieldName := names.New(targetMemberName).Camel + if operation != nil { + fullPath := targetFieldPath + "." + targetMemberName + renamed := cfg.GetResourceFieldName(r.Names.Original, operation.ExportedName, fullPath) + if renamed != fullPath { + targetMemberFieldName = names.New(renamed).Camel + } + } + + sourceMemberName := targetMemberName + if operation != nil { + sourceMemberName = getSourceFieldNameForRename( + cfg, r.Names.Original, operation.ExportedName, + targetFieldPath, targetMemberFieldName, + ) + } + var setCfg *ackgenconfig.SetFieldConfig f, ok := r.Fields[targetFieldPath] if ok { - mf, ok := f.MemberFields[targetMemberName] + mf, ok := f.MemberFields[targetMemberFieldName] if ok { + targetMemberFieldName = mf.Names.Camel setCfg = mf.GetSetterConfig(op) if setCfg != nil && setCfg.IgnoreResourceSetter() { continue @@ -2487,7 +2526,8 @@ func setResourceForUnion( } } - sourceMemberShapeRef = sourceShape.MemberRefs[targetMemberName] + sourceMemberName = resolveSourceMemberName(sourceShape, sourceMemberName) + sourceMemberShapeRef = sourceShape.MemberRefs[sourceMemberName] if sourceMemberShapeRef == nil { continue } @@ -2495,7 +2535,7 @@ func setResourceForUnion( sourceMemberShapeRef.Shape.Type = "union" } - sourceMemberIndex, err := GetMemberIndex(sourceShape, targetMemberName) + sourceMemberIndex, err := GetMemberIndex(sourceShape, sourceMemberName) if err != nil { return "", fmt.Errorf( "resource %q, field %q: could not determine source shape index: %w", @@ -2508,16 +2548,16 @@ func setResourceForUnion( indexedVarName := fmt.Sprintf("%sf%df%d", targetVarName, sourceMemberIndex, sourceMemberIndex) elemVarName := fmt.Sprintf("%sf%d", targetVarName, sourceMemberIndex) sourceMemberShape := sourceMemberShapeRef.Shape - targetMemberCleanNames := names.New(targetMemberName) + targetMemberCleanNames := names.New(targetMemberFieldName) - out += fmt.Sprintf("%scase %sMember%s:\n", indent, sdkGoType, targetMemberName) + out += fmt.Sprintf("%scase %sMember%s:\n", indent, sdkGoType, sourceMemberName) out += fmt.Sprintf( "%s\t%s := %s.(%sMember%s)\n", indent, elemVarName, sourceVarName, sdkGoType, - targetMemberName, + sourceMemberName, ) out += fmt.Sprintf( "%s\tif %s != nil {\n", @@ -2533,7 +2573,7 @@ func setResourceForUnion( switch sourceMemberShape.Type { case "list", "structure", "map", "union": - adaption := setResourceAdaptPrimitiveCollection(sourceMemberShape, qualifiedTargetVar, sourceAdaptedVarName, indent, r.IsSecretField(targetMemberName)) + adaption := setResourceAdaptPrimitiveCollection(sourceMemberShape, qualifiedTargetVar, sourceAdaptedVarName, indent, r.IsSecretField(targetFieldPath+"."+targetMemberFieldName)) out += adaption if adaption != "" { break @@ -2591,3 +2631,90 @@ func setResourceForUnion( return out, nil } + +// getSourceFieldNameForRename finds the original SDK field name for a renamed CRD field +// by searching through the renames configuration for a matching path +func getSourceFieldNameForRename( + cfg *ackgenconfig.Config, + resourceName string, + opID string, + targetFieldPath string, + targetMemberName string, +) string { + if cfg == nil { + return targetMemberName + } + rConfig, ok := cfg.Resources[resourceName] + if !ok { + return targetMemberName + } + if rConfig.Renames == nil { + return targetMemberName + } + oRenames, ok := rConfig.Renames.Operations[opID] + if !ok { + return targetMemberName + } + + // The renames map is: originalPath -> renamedName + // We need to find: renamedName -> originalPath to get the SDK field name + // e.g., "Rules.Statement.IPSetReferenceStatement.ARN" -> "IPSetARN" + // Given: targetFieldPath="Rules.Statement.IPSetReferenceStatement", targetMemberName="IPSetARN" + // We want to find: "ARN" + for originalPath, renamedField := range oRenames.OutputFields { + if renamedField == targetMemberName && + strings.HasPrefix(originalPath, targetFieldPath+".") { + pathParts := strings.Split(originalPath, ".") + return pathParts[len(pathParts)-1] + } + } + + return targetMemberName +} + +// resolveSourceMemberName returns the concrete member key present in sourceShape +// for a preferred member name, handling acronym/casing differences such as +// JSONBody<->JsonBody and URIPath<->UriPath. +func resolveSourceMemberName( + sourceShape *awssdkmodel.Shape, + preferred string, +) string { + if sourceShape == nil { + return preferred + } + if _, ok := sourceShape.MemberRefs[preferred]; ok { + return preferred + } + preferredCamel := names.New(preferred).Camel + for memberName := range sourceShape.MemberRefs { + if strings.EqualFold(memberName, preferred) { + return memberName + } + if names.New(memberName).Camel == preferredCamel { + return memberName + } + } + return preferred +} + +// operationForType returns the CRD operation that corresponds to the supplied +// OpType. Returns nil when the OpType is unknown or unsupported. +func operationForType( + r *model.CRD, + opType model.OpType, +) *awssdkmodel.Operation { + switch opType { + case model.OpTypeCreate: + return r.Ops.Create + case model.OpTypeGet: + return r.Ops.ReadOne + case model.OpTypeList: + return r.Ops.ReadMany + case model.OpTypeUpdate: + return r.Ops.Update + case model.OpTypeDelete: + return r.Ops.Delete + default: + return nil + } +} diff --git a/pkg/generate/code/set_sdk.go b/pkg/generate/code/set_sdk.go index dd27177fa..92ab11490 100644 --- a/pkg/generate/code/set_sdk.go +++ b/pkg/generate/code/set_sdk.go @@ -1192,31 +1192,61 @@ func SetSDKForStruct( indent := strings.Repeat("\t", indentLevel) targetShape := targetShapeRef.Shape + // Convert opType to get the actual operation for rename lookups + operation := operationForType(r, op) + for memberIndex, memberName := range targetShape.MemberNames() { memberShapeRef := targetShape.MemberRefs[memberName] memberShape := memberShapeRef.Shape - cleanMemberNames := names.New(memberName) - cleanMemberName := cleanMemberNames.Camel - sourceAdaptedVarName := sourceVarName + "." + cleanMemberName - memberFieldPath := sourceFieldPath + "." + cleanMemberName - - // todo: To make `ignore` functionality work for all fields that has `ignore` set to `true`, - // we need to add the below logic inside `SetSDK` function. + + // Look up the renamed CRD field name for this SDK field name + crdfieldName := memberName + if operation != nil { + crdfieldName = getTargetFieldNameForRename( + cfg, r.Names.Original, operation.ExportedName, + sourceFieldPath, memberName, + ) + } + explicitRename := crdfieldName != memberName - // To check if the field member has `ignore` set to `true`. - // This condition currently applies only for members of a field whose shape is `structure` + // Resolve the canonical CRD member field name from model metadata so we + // honor acronym/name normalization (e.g. JSONBody) in addition to explicit + // rename configuration. + sourceMemberName := names.New(crdfieldName).Camel var setCfg *ackgenconfig.SetFieldConfig f, ok := r.Fields[sourceFieldPath] if ok { - mf, ok := f.MemberFields[names.New(memberName).Camel] + mf, ok := f.MemberFields[sourceMemberName] + if !ok && !explicitRename { + mf, ok = f.MemberFields[names.New(memberName).Camel] + } + if !ok && !explicitRename { + for _, candidate := range f.MemberFields { + if candidate.ShapeRef != nil && strings.EqualFold(candidate.ShapeRef.OriginalMemberName, memberName) { + mf = candidate + ok = true + break + } + } + } if ok { + sourceMemberName = mf.Names.Camel setCfg = mf.GetSetterConfig(op) if setCfg != nil && setCfg.IgnoreSDKSetter() { continue } } } + + // Construct sourceAdaptedVarName using the CRD field name + sourceAdaptedVarName := sourceVarName + "." + sourceMemberName + memberFieldPath := sourceFieldPath + "." + memberName + + // todo: To make `ignore` functionality work for all fields that has `ignore` set to `true`, + // we need to add the below logic inside `SetSDK` function. + // To check if the field member has `ignore` set to `true`. + // This condition currently applies only for members of a field whose shape is `structure` fallBackName := r.GetMatchingInputShapeFieldName(op, memberName) if fallBackName != "" { sourceAdaptedVarName = sourceVarName + "." + fallBackName @@ -1870,27 +1900,49 @@ func setSDKForUnion( sdkGoType = "svcsdktypes." + targetShape.OriginalShapeName } + operation := operationForType(r, op) + out += fmt.Sprintf("%sisInterfaceSet := false\n", indent) for memberIndex, memberName := range targetShape.MemberNames() { memberShapeRef := targetShape.MemberRefs[memberName] memberShape := memberShapeRef.Shape - cleanMemberNames := names.New(memberName) - cleanMemberName := cleanMemberNames.Camel - sourceAdaptedVarName := sourceVarName + "." + cleanMemberName - memberFieldPath := sourceFieldPath + "." + cleanMemberName + crdfieldName := memberName + if operation != nil { + crdfieldName = getTargetFieldNameForRename( + cfg, r.Names.Original, operation.ExportedName, + sourceFieldPath, memberName, + ) + } + explicitRename := crdfieldName != memberName + sourceMemberName := names.New(crdfieldName).Camel var setCfg *ackgenconfig.SetFieldConfig f, ok := r.Fields[sourceFieldPath] if ok { - mf, ok := f.MemberFields[memberName] + mf, ok := f.MemberFields[sourceMemberName] + if !ok && !explicitRename { + mf, ok = f.MemberFields[names.New(memberName).Camel] + } + if !ok && !explicitRename { + for _, candidate := range f.MemberFields { + if candidate.ShapeRef != nil && strings.EqualFold(candidate.ShapeRef.OriginalMemberName, memberName) { + mf = candidate + ok = true + break + } + } + } if ok { + sourceMemberName = mf.Names.Camel setCfg = mf.GetSetterConfig(op) if setCfg != nil && setCfg.IgnoreSDKSetter() { continue } } } + sourceAdaptedVarName := sourceVarName + "." + sourceMemberName + memberFieldPath := sourceFieldPath + "." + memberName elemVarName := fmt.Sprintf("%sf%dParent", targetVarName, memberIndex) @@ -1968,3 +2020,45 @@ func setSDKForUnion( return out, nil } + +// getTargetFieldNameForRename finds the renamed CRD field name for an SDK field name +// by searching through the renames configuration for a matching path +func getTargetFieldNameForRename( + cfg *ackgenconfig.Config, + resourceName string, + opID string, + sourceFieldPath string, + sourceMemberName string, +) string { + if cfg == nil { + return sourceMemberName + } + rConfig, ok := cfg.Resources[resourceName] + if !ok { + return sourceMemberName + } + if rConfig.Renames == nil { + return sourceMemberName + } + oRenames, ok := rConfig.Renames.Operations[opID] + if !ok { + return sourceMemberName + } + + for originalPath, renamedField := range oRenames.InputFields { + pathParts := strings.Split(originalPath, ".") + if len(pathParts) == 0 { + continue + } + originalMemberName := pathParts[len(pathParts)-1] + if !strings.EqualFold(originalMemberName, sourceMemberName) { + continue + } + originalParentPath := strings.Join(pathParts[:len(pathParts)-1], ".") + if strings.EqualFold(originalParentPath, sourceFieldPath) { + return renamedField + } + } + + return sourceMemberName +} diff --git a/pkg/model/crd.go b/pkg/model/crd.go index 5eb5e0729..e5a826a8f 100644 --- a/pkg/model/crd.go +++ b/pkg/model/crd.go @@ -216,7 +216,7 @@ func (r *CRD) AddSpecField( ) error { fPath := memberNames.Camel fConfig := r.cfg.GetFieldConfigByPath(r.Names.Original, fPath) - f, err := NewField(r, fPath, memberNames, shapeRef, fConfig) + f, err := NewFieldWithRenames(r, fPath, memberNames, shapeRef, fConfig, mergedFieldRenames(r)) if err != nil { return fmt.Errorf("resource %q, field %q: %w", r.Names.Original, fPath, err) } @@ -250,7 +250,7 @@ func (r *CRD) AddStatusField( ) error { fPath := memberNames.Camel fConfig := r.cfg.GetFieldConfigByPath(r.Names.Original, fPath) - f, err := NewField(r, fPath, memberNames, shapeRef, fConfig) + f, err := NewFieldWithRenames(r, fPath, memberNames, shapeRef, fConfig, mergedFieldRenames(r)) if err != nil { return fmt.Errorf("resource %q, field %q: %w", r.Names.Original, fPath, err) } diff --git a/pkg/model/field.go b/pkg/model/field.go index bd9879f23..fd69f1994 100644 --- a/pkg/model/field.go +++ b/pkg/model/field.go @@ -417,7 +417,20 @@ func NewField( shapeRef *awssdkmodel.ShapeRef, cfg *ackgenconfig.FieldConfig, ) (*Field, error) { - return newFieldRecurse(crd, path, make(map[string]struct{}, 0), fieldNames, shapeRef, cfg) + return NewFieldWithRenames(crd, path, fieldNames, shapeRef, cfg, nil) +} + +// NewFieldWithRenames returns a pointer to a new Field object and applies the +// supplied field renames while recursively walking nested members. +func NewFieldWithRenames( + crd *CRD, + path string, + fieldNames names.Names, + shapeRef *awssdkmodel.ShapeRef, + cfg *ackgenconfig.FieldConfig, + renames map[string]string, +) (*Field, error) { + return newFieldRecurse(crd, path, make(map[string]struct{}, 0), fieldNames, shapeRef, cfg, renames) } // newFieldRecurse recursively calls itself with protection against infinite @@ -433,6 +446,7 @@ func newFieldRecurse( fieldNames names.Names, shapeRef *awssdkmodel.ShapeRef, cfg *ackgenconfig.FieldConfig, + renames map[string]string, ) (*Field, error) { memberFields := map[string]*Field{} var gte, gt, gtwp string @@ -485,9 +499,13 @@ func newFieldRecurse( if containerShape.Type == "structure" { // "unpack" the member fields composing this struct field... for _, memberName := range containerShape.MemberNames() { + originalMemberName := memberName + if renamed, ok := renames[originalMemberName]; ok { + memberName = renamed + } cleanMemberNames := names.New(memberName) memberPath := path + "." + cleanMemberNames.Camel - memberShape := containerShape.MemberRefs[memberName] + memberShape := containerShape.MemberRefs[originalMemberName] // Check to see if we have seen this shape before in the stack. // Cyclic references are not supported. @@ -500,7 +518,7 @@ func newFieldRecurse( fConfigs := crd.cfg.GetFieldConfigs(crd.Names.Original) memberField, err := newFieldRecurse( - crd, memberPath, nestedParentFields, cleanMemberNames, memberShape, fConfigs[memberPath], + crd, memberPath, nestedParentFields, cleanMemberNames, memberShape, fConfigs[memberPath], renames, ) if err != nil { return nil, err diff --git a/pkg/model/model.go b/pkg/model/model.go index 8680f9bed..de3c4d24d 100644 --- a/pkg/model/model.go +++ b/pkg/model/model.go @@ -736,6 +736,9 @@ func (m *Model) processNestedFieldTypeDefs( // field paths contains a dot (".") continue } + if err := renameTypeDefAttribute(crd, field, tdefs); err != nil { + return fmt.Errorf("resource %q, field %q: %w", crd.Names.Original, fieldPath, err) + } if field.FieldConfig == nil { // Likewise, we don't need to transform any TypeDef if the // nested field doesn't have a FieldConfig instructing us to @@ -772,6 +775,43 @@ func (m *Model) processNestedFieldTypeDefs( return nil } +// renameTypeDefAttribute updates the TypeDef attribute name to match a renamed +// nested field path. +func renameTypeDefAttribute(crd *CRD, field *Field, tdefs []*TypeDef) error { + if field == nil || field.ShapeRef == nil { + return nil + } + + origMemberName := field.ShapeRef.OriginalMemberName + if origMemberName == "" || strings.EqualFold(origMemberName, field.Names.Original) { + return nil + } + + origFieldPath := strings.TrimSuffix(field.Path, field.Names.Camel) + names.New(origMemberName).Camel + parentTypeDef, fieldAttr, err := getAttributeFromPath(crd, origFieldPath, tdefs) + if err != nil { + // If we can't find the attribute with the original name, it may have already + // been renamed. Return without error. + return nil + } + if fieldAttr == nil || parentTypeDef == nil { + // If we can't find the attribute with the original name, it may have already + // been renamed. Check if it already has the new name. + renamedFieldPath := strings.TrimSuffix(field.Path, field.Names.Camel) + field.Names.Camel + _, renamedAttr, _ := getAttributeFromPath(crd, renamedFieldPath, tdefs) + if renamedAttr != nil { + // Already renamed, nothing to do + return nil + } + return nil + } + + delete(parentTypeDef.Attrs, fieldAttr.Names.Original) + fieldAttr.Names = names.New(field.Names.Original) + parentTypeDef.Attrs[fieldAttr.Names.Original] = fieldAttr + return nil +} + // getAttributeFromPath extracts the parent TypeDef and the target attribute for // the corresponding fieldPath of nested field. This function should only be // called for nested fieldPath. Non-nested fieldPath should be handled by higher @@ -1034,13 +1074,14 @@ func replaceSecretAttrGoType( // data type overridden (e.g. for SecretKeyReferences) func (m *Model) processFields(crds []*CRD) error { for _, crd := range crds { + renames := mergedFieldRenames(crd) for _, fieldName := range crd.SpecFieldNames() { - if err := m.processTopLevelField(crd, crd.SpecFields[fieldName]); err != nil { + if err := m.processTopLevelField(crd, crd.SpecFields[fieldName], renames); err != nil { return err } } for _, fieldName := range crd.StatusFieldNames() { - if err := m.processTopLevelField(crd, crd.StatusFields[fieldName]); err != nil { + if err := m.processTopLevelField(crd, crd.StatusFields[fieldName], renames); err != nil { return err } } @@ -1048,11 +1089,43 @@ func (m *Model) processFields(crds []*CRD) error { return nil } +// mergedFieldRenames collects field rename mappings across all configured +// operations for a resource and returns a single lookup map keyed by original +// field path. +// +// Renames must be globally consistent per resource: if two operations rename +// the same original path to different target names, this function panics to +// surface an invalid generator configuration early. +func mergedFieldRenames(crd *CRD) map[string]string { + renames := map[string]string{} + if crd == nil || crd.Config() == nil { + return renames + } + + for _, op := range crd.Ops.IterOps() { + opRenames := crd.Config().GetAllRenames(crd.Names.Original, map[string]*awssdkmodel.Operation{ + op.ExportedName: op, + }) + for orig, renamed := range opRenames { + if existing, ok := renames[orig]; ok && existing != renamed { + panic(fmt.Sprintf( + "conflicting field renames for resource %q field %q: %q vs %q", + crd.Names.Original, orig, existing, renamed, + )) + } + renames[orig] = renamed + } + } + + return renames +} + // processTopLevelField processes any nested fields (non-scalar fields associated // with the Spec and Status objects) func (m *Model) processTopLevelField( crd *CRD, field *Field, + renames map[string]string, ) error { if field.ShapeRef == nil && !field.IsReference() && (field.FieldConfig == nil || !field.FieldConfig.IsAttribute) { fmt.Printf( @@ -1067,15 +1140,15 @@ func (m *Model) processTopLevelField( fieldType := fieldShape.Type switch fieldType { case "structure": - if err := m.processStructField(crd, field.Path+".", field); err != nil { + if err := m.processStructField(crd, field.Path+".", field, renames); err != nil { return err } case "list": - if err := m.processListField(crd, field.Path+".", field); err != nil { + if err := m.processListField(crd, field.Path+".", field, renames); err != nil { return err } case "map": - if err := m.processMapField(crd, field.Path+".", field); err != nil { + if err := m.processMapField(crd, field.Path+".", field, renames); err != nil { return err } } @@ -1090,27 +1163,35 @@ func (m *Model) processField( parentField *Field, fieldName string, fieldShapeRef *awssdkmodel.ShapeRef, + renames map[string]string, ) error { fieldNames := names.New(fieldName) fieldShape := fieldShapeRef.Shape fieldShapeType := fieldShape.Type fieldPath := parentFieldPath + fieldNames.Camel + + // Check if the full field path has a rename + if renamed, ok := renames[fieldPath]; ok { + fieldNames = names.New(renamed) + fieldPath = parentFieldPath + fieldNames.Camel + } + fieldConfig := crd.Config().GetFieldConfigByPath(crd.Names.Original, fieldPath) - field, err := NewField(crd, fieldPath, fieldNames, fieldShapeRef, fieldConfig) + field, err := NewFieldWithRenames(crd, fieldPath, fieldNames, fieldShapeRef, fieldConfig, renames) if err != nil { return fmt.Errorf("resource %q, field %q: %w", crd.Names.Original, fieldPath, err) } switch fieldShapeType { case "structure": - if err := m.processStructField(crd, fieldPath+".", field); err != nil { + if err := m.processStructField(crd, fieldPath+".", field, renames); err != nil { return err } case "list": - if err := m.processListField(crd, fieldPath+".", field); err != nil { + if err := m.processListField(crd, fieldPath+".", field, renames); err != nil { return err } case "map": - if err := m.processMapField(crd, fieldPath+".", field); err != nil { + if err := m.processMapField(crd, fieldPath+".", field, renames); err != nil { return err } } @@ -1124,11 +1205,12 @@ func (m *Model) processStructField( crd *CRD, fieldPath string, field *Field, + renames map[string]string, ) error { fieldShape := field.ShapeRef.Shape for _, memberName := range fieldShape.MemberNames() { memberRef := fieldShape.MemberRefs[memberName] - if err := m.processField(crd, fieldPath, field, memberName, memberRef); err != nil { + if err := m.processField(crd, fieldPath, field, memberName, memberRef, renames); err != nil { return err } } @@ -1142,6 +1224,7 @@ func (m *Model) processListField( crd *CRD, fieldPath string, field *Field, + renames map[string]string, ) error { fieldShape := field.ShapeRef.Shape elementFieldShape := fieldShape.MemberRef.Shape @@ -1150,7 +1233,7 @@ func (m *Model) processListField( } for _, memberName := range elementFieldShape.MemberNames() { memberRef := elementFieldShape.MemberRefs[memberName] - if err := m.processField(crd, fieldPath, field, memberName, memberRef); err != nil { + if err := m.processField(crd, fieldPath, field, memberName, memberRef, renames); err != nil { return err } } @@ -1164,6 +1247,7 @@ func (m *Model) processMapField( crd *CRD, fieldPath string, field *Field, + renames map[string]string, ) error { fieldShape := field.ShapeRef.Shape valueFieldShape := fieldShape.ValueRef.Shape @@ -1172,7 +1256,7 @@ func (m *Model) processMapField( } for _, memberName := range valueFieldShape.MemberNames() { memberRef := valueFieldShape.MemberRefs[memberName] - if err := m.processField(crd, fieldPath, field, memberName, memberRef); err != nil { + if err := m.processField(crd, fieldPath, field, memberName, memberRef, renames); err != nil { return err } } diff --git a/pkg/model/model_wafv2_test.go b/pkg/model/model_wafv2_test.go index 1db342802..174e0c8b0 100644 --- a/pkg/model/model_wafv2_test.go +++ b/pkg/model/model_wafv2_test.go @@ -79,3 +79,54 @@ func TestWAFv2_SharedShape_CustomNestedFields(t *testing.T) { "%s.Rules.Statement.AndStatement should be string type", crd.name) } } + +func TestWAFv2_RenamesAndReferenceFields(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + g := testutil.NewModelForService(t, "wafv2") + + crds, err := g.GetCRDs() + require.NoError(err) + + // TypeDef-level assertion for rename propagation + typeDef := testutil.GetTypeDefByName(t, g, "IPSetReferenceStatement") + require.NotNil(typeDef) + assert.NotNil(typeDef.GetAttribute("IPSetARN")) + assert.Nil(typeDef.GetAttribute("ARN")) + + typeDef = testutil.GetTypeDefByName(t, g, "RuleGroupReferenceStatement") + require.NotNil(typeDef) + assert.NotNil(typeDef.GetAttribute("RuleGroupARN")) + assert.NotNil(typeDef.GetAttribute("RuleGroupRef")) + assert.Nil(typeDef.GetAttribute("ARN")) + + // Ensure both resources include the shared IPSetReferenceStatement path. + // (The nested members are validated via the shared TypeDef above.) + for _, crdName := range []string{"RuleGroup", "WebACL"} { + crd := getCRDByName(crdName, crds) + require.NotNil(crd, "CRD %s not found", crdName) + + rulesField := crd.Fields["Rules"] + require.NotNil(rulesField, "%s.Rules field not found", crdName) + + stmtField := rulesField.MemberFields["Statement"] + require.NotNil(stmtField, "%s.Rules.Statement field not found", crdName) + + ipsrsField := stmtField.MemberFields["IPSetReferenceStatement"] + require.NotNil(ipsrsField, "%s.Rules.Statement.IPSetReferenceStatement field not found", crdName) + } + + crdName := "WebACL" + crd := getCRDByName(crdName, crds) + require.NotNil(crd, "CRD %s not found", crdName) + + rulesField := crd.Fields["Rules"] + require.NotNil(rulesField, "%s.Rules field not found", crdName) + + stmtField := rulesField.MemberFields["Statement"] + require.NotNil(stmtField, "%s.Rules.Statement field not found", crdName) + + rgrsField := stmtField.MemberFields["RuleGroupReferenceStatement"] + require.NotNil(rgrsField, "%s.Rules.Statement.RuleGroupReferenceStatement field not found", crdName) +} diff --git a/pkg/testdata/models/apis/wafv2/0000-00-00/generator.yaml b/pkg/testdata/models/apis/wafv2/0000-00-00/generator.yaml index feaf7d347..ea8e1c840 100644 --- a/pkg/testdata/models/apis/wafv2/0000-00-00/generator.yaml +++ b/pkg/testdata/models/apis/wafv2/0000-00-00/generator.yaml @@ -35,6 +35,10 @@ resources: type: string set: - ignore: "all" + Rules.Statement.IPSetReferenceStatement.IPSetARN: + references: + resource: IPSet + path: Status.ACKResourceMetadata.ARN Rules.Statement.OrStatement: type: string set: @@ -51,6 +55,21 @@ resources: type: string set: - ignore: "all" + renames: + operations: + CreateRuleGroup: + input_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + GetRuleGroup: + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + UpdateRuleGroup: + input_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN WebACL: fields: Name: @@ -60,15 +79,19 @@ resources: type: string set: - ignore: "all" - Rules.Statement.OrStatement: + Rules.Statement.IPSetReferenceStatement.IPSetARN: + references: + resource: IPSet + path: Status.ACKResourceMetadata.ARN + Rules.Statement.NotStatement: type: string set: - ignore: "all" - Rules.Statement.NotStatement: + Rules.Statement.ManagedRuleGroupStatement.ScopeDownStatement: type: string set: - ignore: "all" - Rules.Statement.ManagedRuleGroupStatement.ScopeDownStatement: + Rules.Statement.OrStatement: type: string set: - ignore: "all" @@ -76,3 +99,27 @@ resources: type: string set: - ignore: "all" + Rules.Statement.RuleGroupReferenceStatement.RuleGroupARN: + references: + resource: RuleGroup + path: Status.ACKResourceMetadata.ARN + renames: + operations: + CreateWebACL: + input_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + Rules.Statement.RuleGroupReferenceStatement.ARN: RuleGroupARN + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + Rules.Statement.RuleGroupReferenceStatement.ARN: RuleGroupARN + GetWebACL: + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + Rules.Statement.RuleGroupReferenceStatement.ARN: RuleGroupARN + UpdateWebACL: + input_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + Rules.Statement.RuleGroupReferenceStatement.ARN: RuleGroupARN + output_fields: + Rules.Statement.IPSetReferenceStatement.ARN: IPSetARN + Rules.Statement.RuleGroupReferenceStatement.ARN: RuleGroupARN