Skip to content

Commit

Permalink
feat(openapi): generated classes implement interfaces
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Nuri <marc@marcnuri.com>
  • Loading branch information
manusa authored Oct 25, 2024
1 parent aa3d3be commit 0806a4e
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,22 @@ func addOrAppend(commentLines []string, prefix, value string) []string {
}
return commentLines
}
func publicInterfaceName(name string) string {
if unicode.IsUpper(rune(name[0])) {
return name
}
return string(unicode.ToUpper(rune(name[0]))) + name[1:]
}

// func processProtobufOneof
// To generate interfaces and extending classes for oneof fields
// This is something extensively used in the Istio API, that uses these as marker interfaces
//
// For processing we'll add +k8s:openapi-gen=x-kubernetes tags that will be later processed by kube-openapi and added to the OpenAPI json spec
func processProtobufOneof(_ *generator.Context, pkg *types.Package, t *types.Type, m *types.Member, memberIndex int) {
publicInterfaceName := func(name string) string {
if unicode.IsUpper(rune(name[0])) {
return name
}
return string(unicode.ToUpper(rune(name[0]))) + name[1:]
}
// Interfaces
protobufOneOf := reflect.StructTag(m.Tags).Get("protobuf_oneof")
if protobufOneOf != "" {
// kube-openapi doesn't handle interfaces, so we need to change the interface to a struct
m.Type.Kind = types.Struct
// Ensure it's exported
t.Members[memberIndex].Type.Name.Name = publicInterfaceName(m.Type.Name.Name)
//// Add comment tag to the referenced type and mark it as an interface
t.Members[memberIndex].Type.CommentLines = append(m.Type.CommentLines, "+k8s:openapi-gen=x-kubernetes-fabric8-type:interface")
// Add comment tag to the current type to mark it as it has fields that are interfaces (useful for the OpenAPI Java generator)
Expand All @@ -119,13 +115,36 @@ func processProtobufOneof(_ *generator.Context, pkg *types.Package, t *types.Typ
}
}

// processProtobufPackageOneOf function to process the protobuf package and change the interfaces to structs
// kube-openapi doesn't handle interfaces, so we need to change the interface to a struct
func processProtobufPackageOneOf(_ *generator.Context, pkg *types.Package) {
for _, t := range pkg.Types {
if t.Kind != types.Interface {
continue
}
openApiGen := gengo.ExtractCommentTags("+", t.CommentLines)["k8s:openapi-gen"]
if len(openApiGen) > 0 {
for _, openApiGenValue := range openApiGen {
if openApiGenValue == "x-kubernetes-fabric8-type:interface" {
// Change to Struct so that it's processed by kube-openapi
t.Kind = types.Struct
// Ensure it's public so that it can be exported
t.Name.Name = publicInterfaceName(t.Name.Name)
break
}
}
}
}
}

// processProtobufTags function to process the protobuf field tags and fix json tags that might have mismatched names for serialization
// This happens for most of the istio APIs
func processProtobufTags(_ *generator.Context, _ *types.Package, t *types.Type, m *types.Member, memberIndex int) {
tags := reflect.StructTag(m.Tags)
protobufTag := tags.Get("protobuf")
jsonTag := tags.Get("json")
if protobufTag != "" && strings.Contains(protobufTag, "json=") {
// TODO, consider also name= (sometimes this is included instad of json=)
name := strings.Split(protobufTag, "json=")[1]
name = strings.Split(name, ",")[0]
var updatedJsonTag string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,25 @@ type GoGenerator struct {
openapiargs.Args
Patterns []string
inputPkgs map[string]bool
processors []func(context *generator.Context, pkg *types.Package, t *types.Type, member *types.Member, memberIndex int)
// memberProcessors are functions that are applied to each member of a type
memberProcessors []func(context *generator.Context, pkg *types.Package, t *types.Type, member *types.Member, memberIndex int)
// packageProcessors are functions that are applied to each package once all type members have been processed for that package
packageProcessors []func(context *generator.Context, pkg *types.Package)
}

func (g *GoGenerator) Generate() error {
g.ReportFilename = g.OutputFile + ".report.txt"
g.processors = []func(context *generator.Context, pkg *types.Package, t *types.Type, member *types.Member, memberIndex int){
g.memberProcessors = []func(context *generator.Context, pkg *types.Package, t *types.Type, member *types.Member, memberIndex int){
processMapKeyTypes,
processOmitPrivateFields,
processPatchComments,
processProtobufOneof,
processProtobufTags,
processSwaggerIgnore,
}
g.packageProcessors = []func(context *generator.Context, pkg *types.Package){
processProtobufPackageOneOf,
}
return gengo.Execute(
generators.NameSystems(),
generators.DefaultNameSystem(),
Expand Down Expand Up @@ -89,11 +95,14 @@ func (g *GoGenerator) processUniverse(context *generator.Context) {
for _, pkg := range context.Universe {
for _, t := range pkg.Types {
for memberIndex, member := range t.Members {
for _, processor := range g.processors {
processor(context, pkg, t, &member, memberIndex)
for _, memeberProcessor := range g.memberProcessors {
memeberProcessor(context, pkg, t, &member, memberIndex)
}
}
}
for _, packageProcessor := range g.packageProcessors {
packageProcessor(context, pkg)
}
}

// Non-deterministic bug-fix
Expand Down
10 changes: 0 additions & 10 deletions kubernetes-model-generator/openapi/maven-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,6 @@
<artifactId>swagger-parser</artifactId>
<version>2.1.23</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down

0 comments on commit 0806a4e

Please sign in to comment.