Skip to content

support field custom documentation strings #394

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 62 additions & 49 deletions pkg/config/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

package config

import "strings"
import (
"strings"
)

// SourceFieldConfig instructs the code generator how to handle a field in the
// Resource's SpecFields/StatusFields collection that takes its value from an
Expand Down Expand Up @@ -49,23 +51,23 @@ import "strings"
// CreateFunction API call, which has a `Code` member of its Input shape that
// looks like this:
//
// "Code": {
// "ImageUri": "string",
// "S3Bucket": "string",
// "S3Key": "string",
// "S3ObjectVersion": "string",
// "ZipFile": blob
// },
// "Code": {
// "ImageUri": "string",
// "S3Bucket": "string",
// "S3Key": "string",
// "S3ObjectVersion": "string",
// "ZipFile": blob
// },
//
// The GetFunction API call's Output shape has a same-named field called
// `Code` in it, but this field looks like this:
//
// "Code": {
// "ImageUri": "string",
// "Location": "string",
// "RepositoryType": "string",
// "ResolvedImageUri": "string"
// },
// "Code": {
// "ImageUri": "string",
// "Location": "string",
// "RepositoryType": "string",
// "ResolvedImageUri": "string"
// },
//
// This presents a conundrum to the ACK code generator, which, as noted above,
// assumes the data types of same-named fields in the Create Operation's Input
Expand All @@ -80,23 +82,24 @@ import "strings"
// ReadOne Operation's Output shape:
//
// resources:
// Function:
// fields:
// CodeLocation:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.Location
// CodeRepositoryType:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.RepositoryType
// CodeRegisteredImageURI:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.RegisteredImageUri
//
// Function:
// fields:
// CodeLocation:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.Location
// CodeRepositoryType:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.RepositoryType
// CodeRegisteredImageURI:
// is_read_only: true
// from:
// operation: GetFunction
// path: Code.RegisteredImageUri
type SourceFieldConfig struct {
// Operation refers to the ID of the API Operation where we will
// determine the field's Go type.
Expand Down Expand Up @@ -135,17 +138,20 @@ type SourceFieldConfig struct {
// the SetResource generator:
//
// ```go
// if resp.DBInstance.DBSecurityGroups != nil {
// f17 := []*string{}
// for _, f17iter := range resp.DBInstance.DBSecurityGroups {
// var f17elem string
// f17elem = *f17iter.DBSecurityGroupName
// f17 = append(f17, &f17elem)
// }
// ko.Spec.DBSecurityGroupNames = f17
// } else {
// ko.Spec.DBSecurityGroupNames = nil
// }
//
// if resp.DBInstance.DBSecurityGroups != nil {
// f17 := []*string{}
// for _, f17iter := range resp.DBInstance.DBSecurityGroups {
// var f17elem string
// f17elem = *f17iter.DBSecurityGroupName
// f17 = append(f17, &f17elem)
// }
// ko.Spec.DBSecurityGroupNames = f17
// } else {
//
// ko.Spec.DBSecurityGroupNames = nil
// }
//
// ```
//
// [0] https://github.com/aws/aws-sdk-go/blob/0a01aef9caf16d869c7340e729080205760dc2a2/models/apis/rds/2014-10-31/api-2.json#L2985
Expand Down Expand Up @@ -274,12 +280,14 @@ type LateInitializeConfig struct {
// Example:
// ```
// Integration:
// fields:
// ApiId:
// references:
// resource: API
// path: Status.APIID
//```
//
// fields:
// ApiId:
// references:
// resource: API
// path: Status.APIID
//
// ```
// The above configuration will result in generation of a new field 'APIRef'
// of type 'AWSResourceReference' for ApiGatewayv2-Integration crd.
// When 'APIRef' field is present in custom resource manifest, reconciler will
Expand Down Expand Up @@ -384,10 +392,15 @@ type FieldConfig struct {
// TODO(jaypipes,crtbry): Figure out if we can roll the CustomShape stuff
// into this type override...
Type *string `json:"type,omitempty"`
// Documentation is a string that is added *in addition to* any existing
// field documentation derived from the field's doc-2.json contents. For
// custom fields, this allows you to add custom documentation for the
// field.
Documentation *string `json:"documentation,omitempty"`
}

// GetFieldConfigs returns all FieldConfigs for a given resource as a map.
// The map is keyed by the resource's field names after applying renames, if applicable.
// The map is keyed by the resource's field paths
func (c *Config) GetFieldConfigs(resourceName string) map[string]*FieldConfig {
if c == nil {
return map[string]*FieldConfig{}
Expand Down
59 changes: 59 additions & 0 deletions pkg/model/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,65 @@ type Field struct {
MemberFields map[string]*Field
}

// GetDocumentation returns a string containing the field's
// description/docstring. If the field has a ShapeRef that has non-empty
// Documentation AND the field has a Documentation configuration option, then
// the docstring contained in the Documentation configuration option will be
// appended to ShapeRef's docstring following 2 line breaks with Go comment
// line beginnings.
//
// In other words, if there is a field with a ShapeRef that has a Documentation string containing:
//
// "// This field contains the identifier for the cluster
//
// // running the cache services"
//
// and the field has a FieldConfig.Documentation string containing:
//
// "please note that this field is updated on the service
//
// side"
//
// then the string returned from this method will be:
//
// "// This field contains the identifier for the cluster
//
// // running the cache services
// //
// // please note that this field is updated on the service
// // side"
func (f *Field) GetDocumentation() string {
hasShapeDoc := false
var sb strings.Builder
if f.ShapeRef != nil {
if f.ShapeRef.Documentation != "" {
hasShapeDoc = true
sb.WriteString(f.ShapeRef.Documentation)
}
}
if f.FieldConfig != nil {
if f.FieldConfig.Documentation != nil {
if hasShapeDoc {
sb.WriteString("\n//\n")
}
// Strip any leading comment slashes from the config option
// docstring since we'll be automatically adding the Go comment
// slashes to the beginning of each new line
cfgDoc := *f.FieldConfig.Documentation
lines := strings.Split(cfgDoc, "\n")
numLines := len(lines)
for x, line := range lines {
sb.WriteString("// ")
sb.WriteString(strings.TrimLeft(line, "/ "))
if x < (numLines - 1) {
sb.WriteString("\n")
}
}
}
}
return sb.String()
}

// IsRequired checks the FieldConfig for Field and returns if the field is
// marked as required or not.A
//
Expand Down
45 changes: 45 additions & 0 deletions pkg/model/field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,51 @@ import (
"github.com/aws-controllers-k8s/code-generator/pkg/testutil"
)

func TestFieldDocumentation(t *testing.T) {
require := require.New(t)

g := testutil.NewModelForServiceWithOptions(t, "ec2",
&testutil.TestingModelOptions{
GeneratorConfigFile: "generator-with-doc-overrides.yaml",
},
)

crds, err := g.GetCRDs()
require.Nil(err)

crd := getCRDByName("LaunchTemplate", crds)
require.NotNil(crd)

specFields := crd.SpecFields

// We have not altered the docstring for LaunchTemplateData from the
// docstring that comes in the doc-2.json file...
ltdField := specFields["LaunchTemplateData"]
require.NotNil(ltdField)
require.NotNil(ltdField.ShapeRef)

require.Equal(
"// The information for the launch template.",
ltdField.GetDocumentation(),
)

// We have added an additional docstring for
// LaunchTemplateData.HibernationOptions.Configured to the docstring that
// comes in the doc-2.json file...
hoField := ltdField.MemberFields["HibernationOptions"]
require.NotNil(hoField)
require.NotNil(hoField.ShapeRef)
hocField := hoField.MemberFields["Configured"]
require.NotNil(hocField)
require.NotNil(hocField.ShapeRef)

require.Equal(
"// If you set this parameter to true, the instance is enabled for hibernation.\n// \n// Default: false\n//\n// XXX extended docs XXX",
hocField.GetDocumentation(),
)

}

func TestMemberFields(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
ignore:
resource_names:
- AccountAttribute
- CapacityReservation
- CarrierGateway
- ClientVpnEndpoint
- ClientVpnRoute
- CustomerGateway
- DefaultSubnet
- DefaultVpc
- DhcpOptions
- EgressOnlyInternetGateway
- Fleet
- FpgaImage
- Image
- Instance
- InstanceExportTask
- InternetGateway
- KeyPair
- LaunchTemplateVersion
#- LaunchTemplate
- LocalGatewayRouteTableVpcAssociation
- LocalGatewayRoute
- ManagedPrefixList
- NatGateway
- NetworkAclEntry
- NetworkAcl
- NetworkInsightsPath
- NetworkInterfacePermission
- NetworkInterface
- PlacementGroup
- ReservedInstancesListing
- RouteTable
- Route
- SecurityGroup
- Snapshot
- SpotDatafeedSubscription
- Subnet
- TrafficMirrorFilterRule
- TrafficMirrorFilter
- TrafficMirrorSession
- TrafficMirrorTarget
- TransitGatewayConnectPeer
- TransitGatewayConnect
- TransitGatewayMulticastDomain
- TransitGatewayPeeringAttachment
- TransitGatewayPrefixListReference
- TransitGatewayRouteTable
- TransitGatewayRoute
- TransitGatewayVpcAttachment
- TransitGateway
- Volume
- VpcEndpointConnectionNotification
- VpcEndpointServiceConfiguration
- VpcEndpoint
- Vpc
- VpcCidrBlock
- VpcPeeringConnection
- VpnConnectionRoute
- VpnConnection
- VpnGateway
resources:
LaunchTemplate:
fields:
LaunchTemplateData.HibernationOptions.Configured:
documentation: XXX extended docs XXX
8 changes: 4 additions & 4 deletions templates/apis/crd.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (
{{ .CRD.Documentation }}
type {{ .CRD.Kind }}Spec struct {
{{ range $fieldName, $field := .CRD.SpecFields }}
{{ if and ($field.ShapeRef) ($field.ShapeRef.Documentation) -}}
{{ $field.ShapeRef.Documentation }}
{{ if $field.GetDocumentation -}}
{{ $field.GetDocumentation }}
{{ end -}}
{{- if and ($field.IsRequired) (not $field.HasReference) -}}
// +kubebuilder:validation:Required
Expand All @@ -40,8 +40,8 @@ type {{ .CRD.Kind }}Status struct {
// +kubebuilder:validation:Optional
Conditions []*ackv1alpha1.Condition `json:"conditions"`
{{- range $fieldName, $field := .CRD.StatusFields }}
{{- if and ($field.ShapeRef) ($field.ShapeRef.Documentation) }}
{{ $field.ShapeRef.Documentation }}
{{- if $field.GetDocumentation }}
{{ $field.GetDocumentation }}
{{- end }}
// +kubebuilder:validation:Optional
{{ $field.Names.Camel }} {{ $field.GoType }} `json:"{{ $field.Names.CamelLower }},omitempty"`
Expand Down