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

Support Enum as Separate Type in OpenAPI Spec for Client Generators #621

Open
neffsvg opened this issue Oct 21, 2024 · 5 comments
Open

Support Enum as Separate Type in OpenAPI Spec for Client Generators #621

neffsvg opened this issue Oct 21, 2024 · 5 comments
Labels
enhancement New feature or request

Comments

@neffsvg
Copy link

neffsvg commented Oct 21, 2024

Is there a way to add enums as separate types in the OpenAPI specification file, rather than using inline enum definitions. This will allow client generators (e.g., TypeScript/JavaScript) to create reusable enum types that can be used by applications like Angular, React, etc.

how "enums" are generated right now:

"ObjectFoo": {
    "properties": {
        "fooType": {
            "enum": [
                "ENUM_Value_ONE",
                "ENUM_Value_TWO",
                "ENUM_Value_THREE"
            ],
            "example": "ENUM_Value_ONE",
            "type": "string"
        }
    }
}

how "enums" could be generated as extra types:

components:
  schemas:
    FooTypeEnum:
      type: string
      enum:
        - ENUM_Value_ONE
        - ENUM_Value_TWO
        - ENUM_Value_THREE

"ObjectFoo": {
    "properties": {
        "fooType": {
            "$ref": "#/components/schemas/FooTypeEnum"
        }
    }
}
@superstas
Copy link
Contributor

@neffsvg +1

It's kinda related to this one #560

@danielgtaylor danielgtaylor added the enhancement New feature or request label Oct 23, 2024
@yursan9
Copy link
Contributor

yursan9 commented Oct 24, 2024

I'm going to see if I can implement this

@lsdch
Copy link
Contributor

lsdch commented Nov 5, 2024

In case that helps, here is a workaround that relies on implementing the SchemaProvider interface for enums

type InstitutionKind string

const (
	Lab                           InstitutionKind = "Lab"
	FoundingAgency      InstitutionKind = "FundingAgency"
	SequencingPlatform InstitutionKind = "SequencingPlatform"
	Other                        InstitutionKind = "Other"
)

var InstitutionKindValues = []InstitutionKind{
	Lab,
	FoundingAgency,
	SequencingPlatform,
	Other,
}

// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
  if r.Map()["InstitutionKind"] == nil {
    schemaRef := r.Schema(reflect.TypeOf(""), true, "InstitutionKind")
    schemaRef.Title = "InstitutionKind"
    for _, v := range InstitutionKindValues {
      schemaRef.Enum = append(schemaRef.Enum, string(v))
    }
    r.Map()["InstitutionKind"] = schemaRef
  }
  return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}

I use code generation to keep the enum values array up to date and avoid typing boilerplate for every enum type.

@jamesleeht
Copy link

Thanks @lsdch - this is super useful.

@jamesleeht
Copy link

jamesleeht commented Dec 13, 2024

If anyone else is using @lsdch solution above, it's likely that the InstitutionKindValues might be a map (especially if your enum is generated).
If this is the case, you can write a simple function to sort the map:

import "sort"

func SortMap[T any](m map[string]T) []string {
	keys := make([]string, 0, len(m))
	for k := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return keys
}
// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
  if r.Map()["InstitutionKind"] == nil {
    schemaRef := r.Schema(reflect.TypeOf(""), true, "InstitutionKind")
    schemaRef.Title = "InstitutionKind"
    for _, v := range SortMap(InstitutionKindValues) {
      schemaRef.Enum = append(schemaRef.Enum, string(v))
    }
    r.Map()["InstitutionKind"] = schemaRef
  }
  return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}

This will stop the openapi.yaml diff from changing each time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants