diff --git a/integration_test.go b/integration_test.go index 6c9e637..f1bdd5e 100644 --- a/integration_test.go +++ b/integration_test.go @@ -139,6 +139,16 @@ func TestOpenAPIGeneration(t *testing.T) { }, wantFiles: []string{"test10/openapiv3.yaml"}, }, + { + name: "Test enum with x-enumNames", + id: "test11", + perPackage: false, + genOpts: "yaml=true,single_file=true,use_int_enums=true,enum_names_extensions=x-enumNames;x-enum-values", + inputFiles: map[string][]string{ + "test11": {"./testdata/test11/enums.proto"}, + }, + wantFiles: []string{"test11/openapiv3.yaml"}, + }, } for _, tc := range testcases { diff --git a/main.go b/main.go index fed1349..fdc21eb 100644 --- a/main.go +++ b/main.go @@ -51,10 +51,12 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes includeDescription := true multilineDescription := false enumAsIntOrString := false + enumAsInt := false protoOneof := false intNative := false disableKubeMarkers := false + var enumNamesExtensions []string var messagesWithEmptySchema []string var ignoredKubeMarkerSubstrings []string @@ -119,6 +121,20 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes default: return nil, fmt.Errorf("unknown value '%s' for enum_as_int_or_string", v) } + } else if k == "use_int_enums" { + switch strings.ToLower(v) { + case "true": + enumAsInt = true + case "false": + enumAsInt = false + default: + return nil, fmt.Errorf("unknown value '%s' for enum_as_int", v) + } + } else if k == "enum_names_extensions" { + enumNamesExtensions = strings.Split(v, ";") + if len(enumNamesExtensions) == 0 { + return nil, fmt.Errorf("cant use '%s' as enum_names_extensions, provide a semicolon separated list", v) + } } else if k == "proto_oneof" { switch strings.ToLower(v) { case "true": @@ -185,6 +201,8 @@ func generate(request pluginpb.CodeGeneratorRequest) (*pluginpb.CodeGeneratorRes useRef, descriptionConfiguration, enumAsIntOrString, + enumAsInt, + enumNamesExtensions, messagesWithEmptySchema, protoOneof, intNative, diff --git a/openapiGenerator.go b/openapiGenerator.go index c7304b7..139adc2 100644 --- a/openapiGenerator.go +++ b/openapiGenerator.go @@ -101,6 +101,10 @@ type openapiGenerator struct { // we need to support this since some controllers marshal enums as integers and others as strings enumAsIntOrString bool + enumsAsInt bool + + enumNamesExtensions []string + // @solo.io customizations to define schemas for certain messages customSchemasByMessageName map[string]openapi3.Schema @@ -138,6 +142,8 @@ func newOpenAPIGenerator( useRef bool, descriptionConfiguration *DescriptionConfiguration, enumAsIntOrString bool, + enumAsInt bool, + enumNamesExtensions []string, messagesWithEmptySchema []string, protoOneof bool, intNative bool, @@ -156,6 +162,8 @@ func newOpenAPIGenerator( useRef: useRef, descriptionConfiguration: descriptionConfiguration, enumAsIntOrString: enumAsIntOrString, + enumsAsInt: enumAsInt, + enumNamesExtensions: enumNamesExtensions, customSchemasByMessageName: buildCustomSchemasByMessageName(messagesWithEmptySchema), protoOneof: protoOneof, intNative: intNative, @@ -569,6 +577,14 @@ func (g *openapiGenerator) generateEnum(enum *protomodel.EnumDescriptor, allSche } func (g *openapiGenerator) generateEnumSchema(enum *protomodel.EnumDescriptor) *openapi3.Schema { + if !g.enumsAsInt { + g.generateStringEnum(enum) + } + + return g.generateIntEnum(enum) +} + +func (g *openapiGenerator) generateStringEnum(enum *protomodel.EnumDescriptor) *openapi3.Schema { /** The out of the box solution created an enum like: enum: @@ -599,6 +615,23 @@ func (g *openapiGenerator) generateEnumSchema(enum *protomodel.EnumDescriptor) * o.Enum = append(o.Enum, v.GetName()) } o.Type = &openapi3.Types{openapi3.TypeString} + return o +} + +func (g *openapiGenerator) generateIntEnum(enum *protomodel.EnumDescriptor) *openapi3.Schema { + o := openapi3.NewIntegerSchema() + o.Description = g.generateDescription(enum) + + values := enum.GetValue() + var names []string + for _, v := range values { + o.Enum = append(o.Enum, v.GetNumber()) + names = append(names, v.GetName()) + } + o.Extensions = make(map[string]interface{}) + for _, extensionName := range g.enumNamesExtensions { + o.Extensions[extensionName] = names + } return o } diff --git a/testdata/golden/test11/openapiv3.yaml b/testdata/golden/test11/openapiv3.yaml new file mode 100644 index 0000000..f4cd403 --- /dev/null +++ b/testdata/golden/test11/openapiv3.yaml @@ -0,0 +1,35 @@ +components: + schemas: + test11.AnotherEnum: + enum: + - 0 + - 1 + - 2 + type: integer + x-enum-values: + - ANOTHER_UNSPECIFIED + - ANOTHER_SOME_NAME + - ANOTHER_ANOTHER_NAME + x-enumNames: + - ANOTHER_UNSPECIFIED + - ANOTHER_SOME_NAME + - ANOTHER_ANOTHER_NAME + test11.MyProtoEnum: + enum: + - 0 + - 1 + - 2 + type: integer + x-enum-values: + - MY_PROTO_UNSPECIFIED + - MY_PROTO_SOME_NAME + - MY_PROTO_ANOTHER_NAME + x-enumNames: + - MY_PROTO_UNSPECIFIED + - MY_PROTO_SOME_NAME + - MY_PROTO_ANOTHER_NAME +info: + title: OpenAPI Spec for Solo APIs. + version: "" +openapi: 3.0.1 +paths: null diff --git a/testdata/test11/enums.proto b/testdata/test11/enums.proto new file mode 100644 index 0000000..5945aa9 --- /dev/null +++ b/testdata/test11/enums.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package test11; + +enum MyProtoEnum { + MY_PROTO_UNSPECIFIED = 0; + MY_PROTO_SOME_NAME = 1; + MY_PROTO_ANOTHER_NAME = 2; +} + +enum AnotherEnum { + ANOTHER_UNSPECIFIED = 0; + ANOTHER_SOME_NAME = 1; + ANOTHER_ANOTHER_NAME = 2; +} \ No newline at end of file