Skip to content

Commit ad5ab13

Browse files
committed
Always use OpenAPIModelName() functions if they are avalable
1 parent d67c058 commit ad5ab13

File tree

8 files changed

+122
-45
lines changed

8 files changed

+122
-45
lines changed

pkg/generators/config.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ func GetOpenAPITargets(context *generator.Context, args *args.Args, boilerplate
6969
newOpenAPIGen(
7070
args.OutputFile,
7171
args.OutputPkg,
72-
len(args.OutputModelNameFile) > 0,
7372
),
7473
newAPIViolationGen(),
7574
}

pkg/generators/openapi.go

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,17 @@ const (
127127
type openAPIGen struct {
128128
generator.GoGenerator
129129
// TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
130-
targetPackage string
131-
imports namer.ImportTracker
132-
useGeneratedModelNames bool
130+
targetPackage string
131+
imports namer.ImportTracker
133132
}
134133

135-
func newOpenAPIGen(outputFilename string, targetPackage string, useGeneratedModelNames bool) generator.Generator {
134+
func newOpenAPIGen(outputFilename string, targetPackage string) generator.Generator {
136135
return &openAPIGen{
137136
GoGenerator: generator.GoGenerator{
138137
OutputFilename: outputFilename,
139138
},
140-
imports: generator.NewImportTrackerForPackage(targetPackage),
141-
targetPackage: targetPackage,
142-
useGeneratedModelNames: useGeneratedModelNames,
139+
imports: generator.NewImportTrackerForPackage(targetPackage),
140+
targetPackage: targetPackage,
143141
}
144142
}
145143

@@ -181,7 +179,7 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
181179
sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
182180

183181
for _, t := range c.Order {
184-
err := newOpenAPITypeWriter(sw, c, g.useGeneratedModelNames).generateCall(t)
182+
err := newOpenAPITypeWriter(sw, c).generateCall(t)
185183
if err != nil {
186184
return err
187185
}
@@ -196,7 +194,7 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
196194
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
197195
klog.V(5).Infof("generating for type %v", t)
198196
sw := generator.NewSnippetWriter(w, c, "$", "$")
199-
err := newOpenAPITypeWriter(sw, c, g.useGeneratedModelNames).generate(t)
197+
err := newOpenAPITypeWriter(sw, c).generate(t)
200198
if err != nil {
201199
return err
202200
}
@@ -235,16 +233,14 @@ type openAPITypeWriter struct {
235233
refTypes map[string]*types.Type
236234
enumContext *enumContext
237235
GetDefinitionInterface *types.Type
238-
useGeneratedModelNames bool
239236
}
240237

241-
func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context, useGeneratedModelNames bool) openAPITypeWriter {
238+
func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context) openAPITypeWriter {
242239
return openAPITypeWriter{
243-
SnippetWriter: sw,
244-
context: c,
245-
refTypes: map[string]*types.Type{},
246-
enumContext: newEnumContext(c),
247-
useGeneratedModelNames: useGeneratedModelNames,
240+
SnippetWriter: sw,
241+
context: c,
242+
refTypes: map[string]*types.Type{},
243+
enumContext: newEnumContext(c),
248244
}
249245
}
250246

@@ -299,6 +295,40 @@ func hasOpenAPIV3OneOfMethod(t *types.Type) bool {
299295
return false
300296
}
301297

298+
func hasOpenAPIModelName(t *types.Type) bool {
299+
for mn, mt := range t.Methods {
300+
if mn != "OpenAPIModelName" {
301+
continue
302+
}
303+
return methodReturnsValue(mt, "", "string")
304+
}
305+
return false
306+
}
307+
308+
func (g openAPITypeWriter) shouldUseOpenAPIModelName(t *types.Type) bool {
309+
// Finds non-generated OpenAPIModelName() functions.
310+
// Generated OpenAPIModelName() are ignored due to the 'ignore_autogenerated' build tag
311+
// but are handled below by checking for use of the +k8s:openapi-model-package.
312+
// This approach allows code generators to be called in any order.
313+
if hasOpenAPIModelName(t) {
314+
return true
315+
}
316+
317+
value, err := extractOpenAPISchemaNamePackage(t.CommentLines)
318+
if err != nil {
319+
klog.Fatalf("Type %v: invalid %s:%v", t, tagModelPackage, err)
320+
}
321+
if value != "" {
322+
return true
323+
}
324+
pkg := g.context.Universe.Package(t.Name.Package)
325+
value, err = extractOpenAPISchemaNamePackage(pkg.Comments)
326+
if err != nil {
327+
klog.Fatalf("Package %v: invalid %s:%v", pkg, tagModelPackage, err)
328+
}
329+
return value != ""
330+
}
331+
302332
// typeShortName returns short package name (e.g. the name x appears in package x definition) dot type name.
303333
func typeShortName(t *types.Type) string {
304334
// `path` vs. `filepath` because packages use '/'
@@ -349,7 +379,7 @@ func (g openAPITypeWriter) generateCall(t *types.Type) error {
349379

350380
args := argsFromType(t)
351381

352-
if g.useGeneratedModelNames {
382+
if g.shouldUseOpenAPIModelName(t) {
353383
g.Do("$.|raw${}.OpenAPIModelName(): ", t)
354384
} else {
355385
// Legacy case: use the "canonical type name"
@@ -685,7 +715,7 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
685715
g.Do("Dependencies: []string{\n", args)
686716
for _, k := range deps {
687717
t := g.refTypes[k]
688-
if g.useGeneratedModelNames {
718+
if g.shouldUseOpenAPIModelName(t) {
689719
g.Do("$.|raw${}.OpenAPIModelName(),", t)
690720
} else {
691721
g.Do("\"$.$\",", k)
@@ -1051,7 +1081,7 @@ func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
10511081

10521082
func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
10531083
g.refTypes[t.Name.String()] = t
1054-
if g.useGeneratedModelNames {
1084+
if g.shouldUseOpenAPIModelName(t) {
10551085
g.Do("Ref: ref($.|raw${}.OpenAPIModelName()),\n", t)
10561086
} else {
10571087
g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())

pkg/generators/openapi_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ func testOpenAPITypeWriter(t *testing.T, cfg *packages.Config) (error, error, *b
6868

6969
callBuffer := &bytes.Buffer{}
7070
callSW := generator.NewSnippetWriter(callBuffer, context, "$", "$")
71-
callError := newOpenAPITypeWriter(callSW, context, false).generateCall(blahT)
71+
callError := newOpenAPITypeWriter(callSW, context).generateCall(blahT)
7272

7373
funcBuffer := &bytes.Buffer{}
7474
funcSW := generator.NewSnippetWriter(funcBuffer, context, "$", "$")
75-
funcError := newOpenAPITypeWriter(funcSW, context, false).generate(blahT)
75+
funcError := newOpenAPITypeWriter(funcSW, context).generate(blahT)
7676

7777
return callError, funcError, callBuffer, funcBuffer, imports.ImportLines()
7878
}

test/integration/naming_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package integration
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
"k8s.io/kube-openapi/pkg/util"
24+
"k8s.io/kube-openapi/pkg/validation/spec"
25+
generated "k8s.io/kube-openapi/test/integration/pkg/generated/namedmodels"
26+
"k8s.io/kube-openapi/test/integration/testdata/namedmodels"
27+
)
28+
29+
func TestCanonicalTypeNames(t *testing.T) {
30+
defs := generated.GetOpenAPIDefinitions(func(path string) spec.Ref {
31+
return spec.MustCreateRef(path)
32+
})
33+
34+
// Define the types we want to check.
35+
typeTestCases := []any{namedmodels.Struct{}, namedmodels.ContainedStruct{}, namedmodels.AtomicStruct{}}
36+
37+
for _, v := range typeTestCases {
38+
t.Run(fmt.Sprintf("%T", v), func(t *testing.T) {
39+
// Get the canonical name using the generator's logic.
40+
canonicalName := util.GetCanonicalTypeName(v)
41+
42+
// Check if the canonical name exists as a key in the generated map.
43+
if _, ok := defs[canonicalName]; !ok {
44+
t.Errorf("canonical type name %q for type %q not found in GetOpenAPIDefinitions", canonicalName, v)
45+
}
46+
})
47+
}
48+
49+
// Additionally, verify that the number of generated definitions matches our expectation.
50+
if len(defs) != len(typeTestCases) {
51+
t.Errorf("expected %d definitions, but got %d", len(typeTestCases), len(defs))
52+
}
53+
}

test/integration/pkg/generated/namedmodels/openapi_generated.go

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
// +k8s:openapi-gen=true
2-
// +k8s:openapi-model-package=io.k8s.kube-openapi.test.integration.testdata.namedmodels
32
package namedmodels
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package namedmodels
22

3-
type Struct struct {
3+
// +k8s:openapi-model-package=io.k8s.kube-openapi.test.integration.testdata.namedmodels
4+
type Struct struct { // The generator should see the +k8s:openapi-model-package and assume that a OpenAPIModelName is (or will be) generated.
45
Field ContainedStruct
56
OtherField int
67
}
78

8-
type ContainedStruct struct{}
9+
type ContainedStruct struct{} // The generator should use the go package name since there is no OpenAPIModelName declared.
910

10-
type AtomicStruct struct {
11+
type AtomicStruct struct { // The generator should respect a manually declared OpenAPIModelName.
1112
Field int
1213
}
14+
15+
// OpenAPIModelName returns the OpenAPI model name for this type.
16+
func (in AtomicStruct) OpenAPIModelName() string {
17+
return "io.k8s.kube-openapi.test.integration.testdata.namedmodels.AtomicStruct"
18+
}

test/integration/testdata/namedmodels/zz_generated_model_name.go

Lines changed: 1 addition & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)