From c573a3d7e93aa99b8717770068b68321e9f3bbb5 Mon Sep 17 00:00:00 2001 From: Amund Tenstad Date: Mon, 7 Aug 2023 14:35:13 +0200 Subject: [PATCH] feat: markers Signed-off-by: Amund Tenstad --- processor/processor.go | 109 +++++++++++++++++++++++++++++-- templates/asciidoctor/type.tpl | 23 +++++-- templates/markdown/type.tpl | 22 +++++-- test/api/v1/guestbook_types.go | 18 ++++-- test/expected.asciidoc | 114 ++++++++++++++++++++++----------- test/expected.md | 111 +++++++++++++++++++++----------- types/types.go | 83 ++++++++++++++++++++++-- 7 files changed, 377 insertions(+), 103 deletions(-) diff --git a/processor/processor.go b/processor/processor.go index fe95de7..e87e9eb 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -21,6 +21,7 @@ import ( gotypes "go/types" "regexp" "sort" + "strconv" "strings" "github.com/elastic/crd-ref-docs/config" @@ -34,9 +35,18 @@ import ( ) const ( - groupNameMarker = "groupName" - objectRootMarker = "kubebuilder:object:root" - versionNameMarker = "versionName" + groupNameMarker = "groupName" + objectRootMarker = "kubebuilder:object:root" + versionNameMarker = "versionName" + defaultMarker = "kubebuilder:default" + validationEnumMarker = "kubebuilder:validation:Enum" + validationFormatMarker = "kubebuilder:validation:Format" + validationMinimumMarker = "kubebuilder:validation:Minimum" + validationMaximumMarker = "kubebuilder:validation:Maximum" + validationMinLengthMarker = "kubebuilder:validation:MinLength" + validationMaxLengthMarker = "kubebuilder:validation:MaxLength" + validationPatternMarker = "kubebuilder:validation:Pattern" + docMarker = "doc:Doc" ) var ignoredCommentRegex = regexp.MustCompile(`\s*^(?i:\+|copyright)`) @@ -62,6 +72,8 @@ func Process(config *config.Config) ([]types.GroupVersionDetails, error) { } p.types.InlineTypes(p.propagateReference) + p.types.PropagateAttributes() + p.applyMarkers() // collect references between types for typeName, refs := range p.references { @@ -124,7 +136,7 @@ func newProcessor(compiledConfig *compiledConfig, maxDepth int) *processor { compiledConfig: compiledConfig, maxDepth: maxDepth, parser: &crd.Parser{ - Collector: &markers.Collector{Registry: &markers.Registry{}}, + Collector: &markers.Collector{Registry: mkRegistry()}, Checker: &loader.TypeChecker{}, }, groupVersions: make(map[schema.GroupVersion]*groupVersionInfo), @@ -270,6 +282,7 @@ func (p *processor) processType(pkg *loader.Package, info *markers.TypeInfo, dep Name: info.Name, Package: pkg.PkgPath, Doc: info.Doc, + Markers: info.Markers, } if p.useRawDocstring && info.RawDecl != nil { @@ -299,6 +312,7 @@ func (p *processor) processType(pkg *loader.Package, info *markers.TypeInfo, dep tmpType.Name = typeDef.Name tmpType.Package = typeDef.Package tmpType.Doc = typeDef.Doc + tmpType.Markers = typeDef.Markers return tmpType } @@ -317,6 +331,7 @@ func (p *processor) processStructFields(parentType *types.Type, pkg *loader.Pack fieldDef := &types.Field{ Name: f.Name, Doc: f.Doc, + Markers: f.Markers, Embedded: f.Name == "", } @@ -354,6 +369,50 @@ func (p *processor) processStructFields(parentType *types.Type, pkg *loader.Pack return parentType } +func parseMarkers(markers markers.MarkerValues) (string, string, []string) { + doc := []string{} + defaultValue := "" + validation := []string{} + + if value, exists := markers[validationMinimumMarker]; exists { + validation = append(validation, "Min: "+strconv.Itoa(value[0].(int))) + } + if value, exists := markers[validationMaximumMarker]; exists { + validation = append(validation, "Max: "+strconv.Itoa(value[0].(int))) + } + if value, exists := markers[validationEnumMarker]; exists { + validation = append(validation, "Values: "+strings.Join(value[0].([]string), ", ")) + } + if value, exists := markers[validationMinLengthMarker]; exists { + validation = append(validation, "MinLength: "+strconv.Itoa(value[0].(int))) + } + if value, exists := markers[validationMaxLengthMarker]; exists { + validation = append(validation, "MaxLength: "+strconv.Itoa(value[0].(int))) + } + if value, exists := markers[validationFormatMarker]; exists { + validation = append(validation, "Format: "+value[0].(string)) + } + if value, exists := markers[validationPatternMarker]; exists { + validation = append(validation, fmt.Sprintf("Pattern: `%s`", value[0].(string))) + } + + if value, exists := markers[defaultMarker]; exists { + defaultValue = fmt.Sprintf("%v", value[0].(Default).Value) + if strings.HasPrefix(defaultValue, "map[") { + defaultValue = strings.TrimPrefix(defaultValue, "map[") + defaultValue = strings.TrimSuffix(defaultValue, "]") + defaultValue = fmt.Sprintf("{ %s }", defaultValue) + } + } + if value, exists := markers[docMarker]; exists { + for _, v := range value { + doc = append(doc, v.(string)) + } + } + + return strings.Join(doc, " "), defaultValue, validation +} + func (p *processor) loadType(pkg *loader.Package, t gotypes.Type, depth int) *types.Type { if depth > p.maxDepth { zap.S().Debugw("Not loading type due to reaching max recursion depth", "type", t.String()) @@ -484,6 +543,7 @@ func (p *processor) loadAliasType(typeDef *types.Type, pkg *loader.Package, unde if bt, ok := underlying.(*gotypes.Basic); ok { typeDef.UnderlyingType = &types.Type{Name: bt.String(), Kind: types.BasicKind} typeDef.Doc = tInfo.Doc + typeDef.Markers = tInfo.Markers return typeDef } @@ -535,6 +595,47 @@ func mkRegistry() *markers.Registry { registry := &markers.Registry{} registry.Define(groupNameMarker, markers.DescribesPackage, "") registry.Define(objectRootMarker, markers.DescribesType, true) + registry.Define(docMarker, markers.DescribesField, "") + registry.Define(validationEnumMarker, markers.DescribesField, []string{}) + registry.Define(validationEnumMarker, markers.DescribesType, []string{}) + registry.Define(validationFormatMarker, markers.DescribesField, "") + registry.Define(validationFormatMarker, markers.DescribesType, "") + registry.Define(validationMinimumMarker, markers.DescribesField, 0) + registry.Define(validationMinimumMarker, markers.DescribesType, 0) + registry.Define(validationMaximumMarker, markers.DescribesField, 0) + registry.Define(validationMaximumMarker, markers.DescribesType, 0) + registry.Define(validationMinLengthMarker, markers.DescribesField, 0) + registry.Define(validationMinLengthMarker, markers.DescribesType, 0) + registry.Define(validationMaxLengthMarker, markers.DescribesField, 0) + registry.Define(validationMaxLengthMarker, markers.DescribesType, 0) + registry.Define(validationPatternMarker, markers.DescribesField, "") + registry.Define(validationPatternMarker, markers.DescribesType, "") registry.Define(versionNameMarker, markers.DescribesPackage, "") + + def, err := markers.MakeAnyTypeDefinition(defaultMarker, markers.DescribesField, Default{}) + registry.Register(markers.Must(def, err)) + def, err = markers.MakeAnyTypeDefinition(defaultMarker, markers.DescribesType, Default{}) + registry.Register(markers.Must(def, err)) + return registry } + +type Default struct { + Value interface{} +} + +func (p *processor) applyMarkers() { + for _, t := range p.types { + var doc string + doc, t.Default, t.Validation = parseMarkers(t.Markers) + if doc != "" { + t.Doc = doc + } + for _, f := range t.Fields { + doc, f.Default, f.Validation = parseMarkers(f.Markers) + if doc != "" { + f.Doc = doc + } + } + } +} diff --git a/templates/asciidoctor/type.tpl b/templates/asciidoctor/type.tpl index 24592b5..49a1188 100644 --- a/templates/asciidoctor/type.tpl +++ b/templates/asciidoctor/type.tpl @@ -7,6 +7,18 @@ {{ $type.Doc }} +{{ if $type.Default -}} +_Default_: {{ $type.Default }} + +{{ end -}} + +{{ if $type.Validation -}} +.Validation: +{{- range $type.Validation }} +- {{ . }} +{{- end }} +{{- end }} + {{ if $type.References -}} .Appears In: **** @@ -17,16 +29,17 @@ {{- end }} {{ if $type.Members -}} -[cols="25a,75a", options="header"] +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description +| Field | Description | Default | Validation {{ if $type.GVK -}} -| *`apiVersion`* __string__ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` -| *`kind`* __string__ | `{{ $type.GVK.Kind }}` +| *`apiVersion`* __string__ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | | +| *`kind`* __string__ | `{{ $type.GVK.Kind }}` | | {{ end -}} {{ range $type.Members -}} -| *`{{ .Name }}`* __{{ asciidocRenderType .Type }}__ | {{ template "type_members" . }} +| *`{{ .Name }}`* __{{ asciidocRenderType .Type }}__ | {{ template "type_members" . }} | {{ .Default }} | {{ range .Validation -}} {{ . }} + +{{ end }} {{ end -}} |=== {{ end -}} diff --git a/templates/markdown/type.tpl b/templates/markdown/type.tpl index c0ac2e0..5a750bd 100644 --- a/templates/markdown/type.tpl +++ b/templates/markdown/type.tpl @@ -8,6 +8,18 @@ {{ $type.Doc }} +{{ if $type.Default -}} +_Default_: {{ $type.Default }} + +{{ end -}} + +{{ if $type.Validation -}} +_Validation:_ +{{- range $type.Validation }} +- {{ . }} +{{- end }} +{{- end }} + {{ if $type.References -}} _Appears in:_ {{- range $type.SortedReferences }} @@ -16,15 +28,15 @@ _Appears in:_ {{- end }} {{ if $type.Members -}} -| Field | Description | -| --- | --- | +| Field | Description | Default | Validation | +| --- | --- | --- | --- | {{ if $type.GVK -}} -| `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` -| `kind` _string_ | `{{ $type.GVK.Kind }}` +| `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | | | +| `kind` _string_ | `{{ $type.GVK.Kind }}` | | | {{ end -}} {{ range $type.Members -}} -| `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | +| `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ .Default }} | {{ range .Validation -}} {{ . }}
{{ end }} | {{ end -}} {{ end -}} diff --git a/test/api/v1/guestbook_types.go b/test/api/v1/guestbook_types.go index 0e12a79..fe2c0b3 100644 --- a/test/api/v1/guestbook_types.go +++ b/test/api/v1/guestbook_types.go @@ -51,16 +51,19 @@ type EmbeddedX struct { X string `json:"x,omitempty"` } -// NOTE: Rating is placed here to ensure that it is parsed as a standalone type +// NOTE: PositiveInt is placed here to ensure that it is parsed as a standalone type // before it is parsed as a struct field. -// Rating is the rating provided by a guest. -type Rating string +// PositiveInt is a positie integer +//+kubebuilder:default=1 +//+kubebuilder:validation:Minimum=1 +type PositiveInt int // GuestbookSpec defines the desired state of Guestbook. type GuestbookSpec struct { // Page indicates the page number - Page *int `json:"page,omitempty"` + //+kubebuilder:default=1 + Page *PositiveInt `json:"page,omitempty"` // Entries contain guest book entries for the page Entries []GuestbookEntry `json:"entries,omitempty"` // Selector selects something @@ -79,10 +82,13 @@ type GuestbookEntry struct { Time metav1.Time `json:"time,omitempty"` // Comment by guest Comment string `json:"comment,omitempty"` - // Rating provided by the guest - Rating Rating `json:"rating,omitempty"` + Rating Rating `json:"rating,omitempty"` } +// Rating is the rating provided by a guest. +// +kubebuilder:validation:MaxLength=1023 +type Rating string + // GuestbookStatus defines the observed state of Guestbook. type GuestbookStatus struct { Status string `json:"status"` diff --git a/test/expected.asciidoc b/test/expected.asciidoc index 871eab6..cabf324 100644 --- a/test/expected.asciidoc +++ b/test/expected.asciidoc @@ -27,19 +27,21 @@ Package v1 contains API Schema definitions for the webapp v1 API group -[cols="25a,75a", options="header"] + + +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` -| *`kind`* __string__ | `Embedded` +| Field | Description | Default | Validation +| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` | | +| *`kind`* __string__ | `Embedded` | | | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. - -| *`a`* __string__ | -| *`b`* __string__ | -| *`c`* __string__ | -| *`x`* __string__ | -| *`d`* __string__ | -| *`e`* __string__ | + | | +| *`a`* __string__ | | | +| *`b`* __string__ | | | +| *`c`* __string__ | | | +| *`x`* __string__ | | | +| *`d`* __string__ | | | +| *`e`* __string__ | | | |=== @@ -48,6 +50,8 @@ Package v1 contains API Schema definitions for the webapp v1 API group + + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-embedded[$$Embedded$$] @@ -57,10 +61,10 @@ Package v1 contains API Schema definitions for the webapp v1 API group - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-embedded4[$$Embedded4$$] **** -[cols="25a,75a", options="header"] +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`x`* __string__ | +| Field | Description | Default | Validation +| *`x`* __string__ | | | |=== @@ -69,19 +73,21 @@ Package v1 contains API Schema definitions for the webapp v1 API group Guestbook is the Schema for the guestbooks API. + + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbooklist[$$GuestbookList$$] **** -[cols="25a,75a", options="header"] +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` -| *`kind`* __string__ | `Guestbook` +| Field | Description | Default | Validation +| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` | | +| *`kind`* __string__ | `Guestbook` | | | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. - -| *`spec`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$]__ | + | | +| *`spec`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$]__ | GuestbookSpec defines the desired state of Guestbook. | | |=== @@ -90,18 +96,21 @@ Guestbook is the Schema for the guestbooks API. GuestbookEntry defines an entry in a guest book. + + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$] **** -[cols="25a,75a", options="header"] +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`name`* __string__ | Name of the guest (pipe \| should be escaped) -| *`time`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta[$$Time$$]__ | Time of entry -| *`comment`* __string__ | Comment by guest -| *`rating`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-rating[$$Rating$$]__ | Rating provided by the guest +| Field | Description | Default | Validation +| *`name`* __string__ | Name of the guest (pipe \| should be escaped) | | +| *`time`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta[$$Time$$]__ | Time of entry | | +| *`comment`* __string__ | Comment by guest | | +| *`rating`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-rating[$$Rating$$]__ | Rating is the rating provided by a guest. | | MaxLength: 1023 + + |=== @@ -110,6 +119,8 @@ GuestbookEntry defines an entry in a guest book. GuestbookHeaders are strings to include at the top of a page. + + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$] @@ -124,14 +135,16 @@ GuestbookList contains a list of Guestbook. -[cols="25a,75a", options="header"] + + +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` -| *`kind`* __string__ | `GuestbookList` +| Field | Description | Default | Validation +| *`apiVersion`* __string__ | `webapp.test.k8s.elastic.co/v1` | | +| *`kind`* __string__ | `GuestbookList` | | | *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#listmeta-v1-meta[$$ListMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`. - -| *`items`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbook[$$Guestbook$$] array__ | + | | +| *`items`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbook[$$Guestbook$$] array__ | Guestbook is the Schema for the guestbooks API. | | |=== @@ -140,29 +153,52 @@ GuestbookList contains a list of Guestbook. GuestbookSpec defines the desired state of Guestbook. + + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbook[$$Guestbook$$] **** -[cols="25a,75a", options="header"] +[cols="20a,50a,15a,15a", options="header"] |=== -| Field | Description -| *`page`* __integer__ | Page indicates the page number -| *`entries`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookentry[$$GuestbookEntry$$] array__ | Entries contain guest book entries for the page -| *`selector`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta[$$LabelSelector$$]__ | Selector selects something -| *`headers`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookheader[$$GuestbookHeader$$] array__ | Headers contains a list of header items to include in the page -| *`certificateRef`* __link:https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference[$$SecretObjectReference$$]__ | CertificateRef is a reference to a secret containing a certificate +| Field | Description | Default | Validation +| *`page`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-positiveint[$$PositiveInt$$]__ | Page indicates the page number | 1 | Min: 1 + + +| *`entries`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookentry[$$GuestbookEntry$$] array__ | Entries contain guest book entries for the page | | +| *`selector`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta[$$LabelSelector$$]__ | Selector selects something | | +| *`headers`* __xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookheader[$$GuestbookHeader$$] array__ | Headers contains a list of header items to include in the page | | +| *`certificateRef`* __link:https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference[$$SecretObjectReference$$]__ | CertificateRef is a reference to a secret containing a certificate | | |=== +[id="{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-positiveint"] +==== PositiveInt (integer) + +PositiveInt is a positie integer + +_Default_: 1 + +.Validation: +- Min: 1 + +.Appears In: +**** +- xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookspec[$$GuestbookSpec$$] +**** + + + [id="{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-rating"] ==== Rating (string) Rating is the rating provided by a guest. +.Validation: +- MaxLength: 1023 + .Appears In: **** - xref:{anchor_prefix}-github-com-elastic-crd-ref-docs-api-v1-guestbookentry[$$GuestbookEntry$$] diff --git a/test/expected.md b/test/expected.md index ac7906b..f3243b4 100644 --- a/test/expected.md +++ b/test/expected.md @@ -23,17 +23,19 @@ Package v1 contains API Schema definitions for the webapp v1 API group -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` -| `kind` _string_ | `Embedded` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `a` _string_ | | -| `b` _string_ | | -| `c` _string_ | | -| `x` _string_ | | -| `d` _string_ | | -| `e` _string_ | | + + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` | | | +| `kind` _string_ | `Embedded` | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `a` _string_ | | | | +| `b` _string_ | | | | +| `c` _string_ | | | | +| `x` _string_ | | | | +| `d` _string_ | | | | +| `e` _string_ | | | | #### EmbeddedX @@ -42,6 +44,8 @@ Package v1 contains API Schema definitions for the webapp v1 API group + + _Appears in:_ - [Embedded](#embedded) - [Embedded1](#embedded1) @@ -49,9 +53,9 @@ _Appears in:_ - [Embedded3](#embedded3) - [Embedded4](#embedded4) -| Field | Description | -| --- | --- | -| `x` _string_ | | +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `x` _string_ | | | | #### Guestbook @@ -60,15 +64,17 @@ _Appears in:_ Guestbook is the Schema for the guestbooks API. + + _Appears in:_ - [GuestbookList](#guestbooklist) -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` -| `kind` _string_ | `Guestbook` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[GuestbookSpec](#guestbookspec)_ | | +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` | | | +| `kind` _string_ | `Guestbook` | | | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[GuestbookSpec](#guestbookspec)_ | GuestbookSpec defines the desired state of Guestbook. | | | #### GuestbookEntry @@ -77,15 +83,17 @@ _Appears in:_ GuestbookEntry defines an entry in a guest book. + + _Appears in:_ - [GuestbookSpec](#guestbookspec) -| Field | Description | -| --- | --- | -| `name` _string_ | Name of the guest (pipe | should be escaped) | -| `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | -| `comment` _string_ | Comment by guest | -| `rating` _[Rating](#rating)_ | Rating provided by the guest | +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `name` _string_ | Name of the guest (pipe | should be escaped) | | | +| `time` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#time-v1-meta)_ | Time of entry | | | +| `comment` _string_ | Comment by guest | | | +| `rating` _[Rating](#rating)_ | Rating is the rating provided by a guest. | | MaxLength: 1023
| #### GuestbookHeader @@ -94,6 +102,8 @@ _Underlying type:_ `string` GuestbookHeaders are strings to include at the top of a page. + + _Appears in:_ - [GuestbookSpec](#guestbookspec) @@ -107,12 +117,14 @@ GuestbookList contains a list of Guestbook. -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` -| `kind` _string_ | `GuestbookList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[Guestbook](#guestbook) array_ | | + + +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `apiVersion` _string_ | `webapp.test.k8s.elastic.co/v1` | | | +| `kind` _string_ | `GuestbookList` | | | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[Guestbook](#guestbook) array_ | Guestbook is the Schema for the guestbooks API. | | | #### GuestbookSpec @@ -121,17 +133,35 @@ GuestbookList contains a list of Guestbook. GuestbookSpec defines the desired state of Guestbook. + + _Appears in:_ - [Guestbook](#guestbook) -| Field | Description | -| --- | --- | -| `page` _integer_ | Page indicates the page number | -| `entries` _[GuestbookEntry](#guestbookentry) array_ | Entries contain guest book entries for the page | -| `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta)_ | Selector selects something | -| `headers` _[GuestbookHeader](#guestbookheader) array_ | Headers contains a list of header items to include in the page | -| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef is a reference to a secret containing a certificate | +| Field | Description | Default | Validation | +| --- | --- | --- | --- | +| `page` _[PositiveInt](#positiveint)_ | Page indicates the page number | 1 | Min: 1
| +| `entries` _[GuestbookEntry](#guestbookentry) array_ | Entries contain guest book entries for the page | | | +| `selector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.25/#labelselector-v1-meta)_ | Selector selects something | | | +| `headers` _[GuestbookHeader](#guestbookheader) array_ | Headers contains a list of header items to include in the page | | | +| `certificateRef` _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.SecretObjectReference)_ | CertificateRef is a reference to a secret containing a certificate | | | + + + + +#### PositiveInt + +_Underlying type:_ `integer` +PositiveInt is a positie integer + +_Default_: 1 + +_Validation:_ +- Min: 1 + +_Appears in:_ +- [GuestbookSpec](#guestbookspec) @@ -141,6 +171,9 @@ _Underlying type:_ `string` Rating is the rating provided by a guest. +_Validation:_ +- MaxLength: 1023 + _Appears in:_ - [GuestbookEntry](#guestbookentry) diff --git a/types/types.go b/types/types.go index b25e664..f95da91 100644 --- a/types/types.go +++ b/types/types.go @@ -24,6 +24,7 @@ import ( "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-tools/pkg/markers" ) // Kind describes the kind of the type (alias, array, etc.) @@ -102,6 +103,9 @@ type Type struct { Name string `json:"name"` Package string `json:"package"` Doc string `json:"doc"` + Default string `json:"default"` + Validation []string `json:"validation"` + Markers markers.MarkerValues `json:"markers"` GVK *schema.GroupVersionKind `json:"gvk"` Kind Kind `json:"kind"` Imported bool `json:"imported"` @@ -223,6 +227,33 @@ func (t *Type) ContainsInlinedTypes() bool { // TypeMap is a map of Type elements type TypeMap map[string]*Type +// PropagateAttributes propagates doc and markers from underlying types to +// parent types and fields. In the following example, the three validation rules +// and the doc for the type Name will be copied to the Spec's From and To +// fields. +// +// //+kubebuilder:object:root=true +// +// type Spec struct { +// From *Name `json:"from,omitempty"` +// To *Name `json:"to,omitempty"` +// } +// +// //+kubebuilder:validation:MinLength=1 +// //+kubebuilder:validation:MaxLength=63 +// //+kubebuilder:validation:Pattern=`^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$` +// +// // Name +// type Name string +func (types TypeMap) PropagateAttributes() { + for _, t := range types { + t.propagateAttributes() + for _, f := range t.Fields { + f.propagateAttributes() + } + } +} + func (types TypeMap) InlineTypes(propagateReference func(original *Type, additional *Type)) { // If C is inlined in B, and B is inlined in A; the fields of C are copied // into B before the fields of B is copied into A. The ideal order of @@ -265,17 +296,59 @@ func (types TypeMap) InlineTypes(propagateReference func(original *Type, additio zap.S().Warnw("Failed to inline all inlined types", "remaining", numTypesToBeInlined) } +func (t *Type) propagateAttributes() { + if t.UnderlyingType == nil { + return + } + + switch t.Kind { + case AliasKind, PointerKind: + t.UnderlyingType.propagateAttributes() + + if t.Doc == "" { + t.Doc = t.UnderlyingType.Doc + } + if t.Markers == nil { + t.Markers = make(markers.MarkerValues, len(t.UnderlyingType.Markers)) + } + for k, m := range t.UnderlyingType.Markers { + if _, exists := t.Markers[k]; !exists { + t.Markers[k] = m + } + } + } +} + // Field describes a field in a struct. type Field struct { - Name string - Embedded bool - Inlined bool - Doc string - Type *Type + Name string + Embedded bool + Inlined bool + Doc string + Default string + Validation []string + Markers markers.MarkerValues + Type *Type } type Fields []*Field +func (f *Field) propagateAttributes() { + f.Type.propagateAttributes() + + if f.Doc == "" { + f.Doc = f.Type.Doc + } + if f.Markers == nil { + f.Markers = make(markers.MarkerValues, len(f.Type.Markers)) + } + for k, m := range f.Type.Markers { + if _, exists := f.Markers[k]; !exists { + f.Markers[k] = m + } + } +} + // inlineType replaces field at index i with the fields of inlined type. func (fields *Fields) inlineType(i int, inlined *Type) { new := make([]*Field, 0, len(*fields)+len(inlined.Fields)-1)