Skip to content
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

refactor: add out enum types #29

Merged
merged 1 commit into from
Mar 14, 2024
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
2 changes: 1 addition & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,5 @@ func TestServiceCreate(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, out)
assert.Equal(t, "wow", out.Plan)
assert.Equal(t, "RUNNING", out.State)
assert.Equal(t, service.ServiceStateTypeRunning, out.State)
}
34 changes: 11 additions & 23 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,6 +319,7 @@ 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
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
Loading