Skip to content

Commit

Permalink
refactor: add out enum types
Browse files Browse the repository at this point in the history
  • Loading branch information
byashimov committed Mar 1, 2024
1 parent 2043ba8 commit a7613bd
Show file tree
Hide file tree
Showing 28 changed files with 2,789 additions and 1,133 deletions.
1 change: 0 additions & 1 deletion Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ tasks:
- curl -s -o openapi.json https://api.aiven.io/doc/openapi.json
go-generate:
cmds:
- rm -rf {{.GEN_OUT_DIR}}
- GEN_OUT_DIR={{.GEN_OUT_DIR}} go run -tags=generator ./generator/...
generate:
cmds:
Expand Down
36 changes: 12 additions & 24 deletions generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/dave/jennifer/jen"
"github.com/iancoleman/strcase"
"github.com/kelseyhightower/envconfig"
"golang.org/x/exp/maps"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -125,17 +124,14 @@ func exec() error {
continue
}

if param.Name == "version_id" {
param.Schema.Type = SchemaTypeInteger
}

param.Ref = ref.Ref
params = append(params, param)
}

if strings.HasSuffix(p.Path, versionIDParam) {
params = append(params, &Parameter{
Name: "version_id",
Schema: &Schema{Type: SchemaTypeInteger},
})
}

p.Parameters = params
}
}
Expand Down Expand Up @@ -323,10 +319,11 @@ func exec() error {
block = append(block, ifErr)

outReturn := jen.Id("out")

if rsp.CamelName != schemaOut.CamelName {
// Takes original name and turns to camel.
// "CamelName" field might have been modified because of name collisions
outReturn.Dot(strcase.ToCamel(rsp.name))
outReturn.Dot(strcase.ToCamel(schemaOut.propertyNames[0]))

if forcePointer {
// return &out.Foo
Expand All @@ -346,13 +343,10 @@ func exec() error {
file.Add(structMeth.Block(block...))
}

scopeValues := maps.Values(scope)
sort.Slice(scopeValues, func(i, j int) bool {
return scopeValues[i].CamelName < scopeValues[j].CamelName
})

for _, v := range scopeValues {
for _, k := range sortedKeys(scope) {
v := scope[k]
err = writeStruct(file, v)

if err != nil {
return err
}
Expand Down Expand Up @@ -395,10 +389,6 @@ var reMakesSense = regexp.MustCompile(`\w`)

// nolint:funlen // It's a generator, it's supposed to be long, and we won't expand it.
func writeStruct(f *jen.File, s *Schema) error {
if s.isMap() || s.isArray() || s.isScalar() && !s.isEnum() {
return nil
}

// nolint:nestif // It's a generator, it's supposed to be long, and we won't expand it.
if s.isEnum() {
kind := getScalarType(s)
Expand Down Expand Up @@ -436,11 +426,9 @@ func writeStruct(f *jen.File, s *Schema) error {

o.Line().Const().Defs(enums...)

if !s.isOut() {
o.Line().Func().Id(s.CamelName + "Choices").Params().Index().Add(kind).Block(
jen.Return(jen.Index().Add(kind).Values(values...)),
)
}
o.Line().Func().Id(s.CamelName + "Choices").Params().Index().Add(kind).Block(
jen.Return(jen.Index().Add(kind).Values(values...)),
)

return nil
}
Expand Down
106 changes: 70 additions & 36 deletions generator/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ package main
import (
"encoding/json"
"fmt"
"regexp"
"sort"
"strings"

"github.com/dave/jennifer/jen"
"github.com/iancoleman/strcase"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
)

const docSite = "https://api.aiven.io/doc"
Expand Down Expand Up @@ -143,7 +145,7 @@ type Schema struct {
in, out bool // Request or Response DTO
}

// nolint:funlen,gocognit // It is easy to maintain and read, we don't need to split it
// nolint:funlen,gocognit,gocyclo // It is easy to maintain and read, we don't need to split it
func (s *Schema) init(doc *Doc, scope map[string]*Schema, name string) {
if s.Ref != "" {
other, err := doc.getSchema(s.Ref)
Expand Down Expand Up @@ -175,13 +177,13 @@ func (s *Schema) init(doc *Doc, scope map[string]*Schema, name string) {
if s.isEnum() {
const enumTypeSuffix = "Type"

if !strings.HasSuffix(s.CamelName, enumTypeSuffix) {
s.CamelName += enumTypeSuffix
betterName := getEnumName(s)
if betterName != s.name {
s.CamelName = cleanEnumName.ReplaceAllString(strcase.ToCamel(betterName), "") + s.CamelName
}

// When it is just "Type" it is useless
if s.CamelName == enumTypeSuffix {
s.CamelName = s.parent.CamelName + s.CamelName
if !strings.Contains(s.CamelName, enumTypeSuffix) {
s.CamelName += enumTypeSuffix
}
}

Expand All @@ -194,6 +196,10 @@ func (s *Schema) init(doc *Doc, scope map[string]*Schema, name string) {
}
}

// Cleans duplicates like StatusStatus
s.CamelName = dedupCamelName(s.CamelName)

// Makes structure private
if s.isPrivate() {
s.CamelName = lowerFirst(s.CamelName)
}
Expand All @@ -202,6 +208,22 @@ func (s *Schema) init(doc *Doc, scope map[string]*Schema, name string) {
s.CamelName = strcase.ToCamel(s.parent.CamelName)
}

// Some cases just impossible to cover
switch s.CamelName {
case "MessageFormatVersionValueType":
s.CamelName = "MessageFormatVersionType"
case "ServiceKafkaConnectConnectorStatusStateType":
s.CamelName = "ServiceKafkaConnectConnectorStateType"
case "PeeringConnectionStateType", "VpcPeeringConnectionWithResourceGroupStateType",
"VpcPeeringConnectionWithRegionStateType":
s.CamelName = "VpcPeeringConnectionStateType"
case "ServiceSchemaRegistryGlobalConfigGetOut", "ServiceSchemaRegistryGlobalConfigPutOut",
"ServiceSchemaRegistrySubjectConfigGetOut", "ServiceSchemaRegistrySubjectConfigPutOut":
if s.isEnum() {
s.CamelName = "CompatibilityType"
}
}

if s.Type == SchemaTypeString {
parts := strings.Split(s.name, "_")
suffix := parts[len(parts)-1]
Expand All @@ -226,39 +248,22 @@ func (s *Schema) init(doc *Doc, scope map[string]*Schema, name string) {
}
}

if s.isObject() {
keys := sortedKeys(scope)
outer:
for len(keys) > 0 {
for _, k := range keys {
other := scope[k]
if other.hash() == s.hash() {
continue
}

// A duplicate
if other.CamelName == s.CamelName {
s.CamelName += "Alt"

continue outer
}
if s.isObject() || s.isEnum() {
for s.parent != nil {
v, ok := scope[s.CamelName]
if !ok {
break
}

break outer
}

scope[s.hash()] = s
}
if v.hash() == s.hash() {
// This is a duplicate
return
}

if s.isEnum() {
// Enums compared by enum list
// In case if they are equal, they must have the same name
other, ok := scope[s.hash()]
if ok {
s.CamelName = other.CamelName
} else {
scope[s.hash()] = s
s.CamelName += "Alt"
}

scope[s.CamelName] = s
}
}

Expand Down Expand Up @@ -297,7 +302,7 @@ func (s *Schema) isMap() bool {
}

func (s *Schema) isEnum() bool {
return len(s.Enum) != 0 && s.isIn()
return len(s.Enum) != 0
}

func (s *Schema) root() *Schema {
Expand All @@ -308,10 +313,12 @@ func (s *Schema) root() *Schema {
return s.parent.root()
}

// isIn is request object
func (s *Schema) isIn() bool {
return s.root().in
}

// isOut is response object
func (s *Schema) isOut() bool {
return s.root().out
}
Expand Down Expand Up @@ -407,3 +414,30 @@ func sortedKeys[T any](m map[string]T) []string {

return keys
}

var cleanEnumName = regexp.MustCompile("(Create|Get|Update|Delete|Stop|Cancel|Verify|Put)")

// getEnumName enum can't have just "state" name, drills to the root until finds something
func getEnumName(s *Schema) string {
switch s.name {
case "type", "value", "state", "status":
if s.parent != nil {
return getEnumName(s.parent)
}
}

return s.name
}

var camelFinder = regexp.MustCompile("[A-Z]+[a-z]+")

func dedupCamelName(src string) string {
result := make([]string, 0)
for _, s := range camelFinder.FindAllString(src, -1) {
if !slices.Contains(result, s) {
result = append(result, s)
}
}

return strings.Join(result, "")
}
Loading

0 comments on commit a7613bd

Please sign in to comment.