Skip to content

OAS3.1.0 const naming is long and different than other types #2596

Open

Description

@deitch

I have a simple oas3 file v3.1.0 with both const and regular objects. Here is my config cfg-schemas.yaml:

package: api
generate:
  models: true
output: go/api/schemas.go
output-options:
  skip-prune: true
  name-normalizer: ToCamelCaseWithInitialisms
  yaml-tags: true

And my schemas input file (simplified) schemas.yaml:

openapi: "3.1.0"
components:
  schemas:
    SomeConst:
      type: string
      const: "someconststringinput/fields/aes256"
    OtherConst:
      type: string
      const: "otherconststringinput/subdata/ed25519"  
    Metadata:
      type: object
      properties:
        name:
          type: string
          description: name of the input
        description:
          type: string
          description: description of the input
        digest:
          type: string
          description: SHA256 digest of the input

And then I run:

oapi-codegen -config ./cfg-schemas.yaml ./schemas.yaml

The output I get is the following:

// Package api provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.8.0 DO NOT EDIT.
package api

// Defines values for OtherConst.
const (
        OtherconststringinputSubdataEd25519 OtherConst = "otherconststringinput/subdata/ed25519"
)

// Valid indicates whether the value is a known member of the OtherConst enum.
func (e OtherConst) Valid() bool {
        switch e {
        case OtherconststringinputSubdataEd25519:
                return true
        default:
                return false
        }
}

// Defines values for SomeConst.
const (
        SomeconststringinputFieldsAes256 SomeConst = "someconststringinput/fields/aes256"
)

// Valid indicates whether the value is a known member of the SomeConst enum.
func (e SomeConst) Valid() bool {
        switch e {
        case SomeconststringinputFieldsAes256:
                return true
        default:
                return false
        }
}

// Metadata defines model for Metadata.
type Metadata struct {
        // Description description of the input
        Description *string `json:"description,omitempty" yaml:"description,omitempty"`

        // Digest SHA256 digest of the input
        Digest *string `json:"digest,omitempty" yaml:"digest,omitempty"`

        // Name name of the input
        Name *string `json:"name,omitempty" yaml:"name,omitempty"`
}

// OtherConst defines model for OtherConst.
type OtherConst string

// SomeConst defines model for SomeConst.
type SomeConst string

Notice that my Metadata component becomes type Metadata struct, while my peer SomeConst constant become type SomeConst string which can be access via the painfully named SomeconststringinputFieldsAes256 SomeConst = "someconststringinput/fields/aes256".

Since this is a constant, I don't care much to create a type SomeConst which is just a string, and then have a single object with a long name SomeconststringinputFieldsAes256 (looks like derived from the const value?) for it. I really am just looking for the simple:

const SomeConst = "someconststringinput/fields/aes256"

I hope this describes the issue well?

Activity

  1. mromaszewicz commented on Sep 29, 2026

    @mromaszewicz
    Member

    We can't tell your intent from your spec. You're asking for a type named SomeConst which is a string and we provide it, and it has only one value, so we go through our standard enum generation.

    OpenAPI 3.1 has an idiom for declaring consts via oneof, here's one of our tests:
    https://github.com/oapi-codegen/oapi-codegen/blob/main/internal/test/openapi31/spec.yaml#L96

    The title will go directly into code using that name. You could do something like this in your spec:

    components:
      schemas:
         Cipher:
            type: string
            oneOf:
               - title: SomeConst
                  const: "someconststringinput/fields/aes256"
               - title: OtherConst
                  const: "otherconststringinput/subdata/ed25519"

    The code will end up looking like this:
    https://github.com/oapi-codegen/oapi-codegen/blob/main/internal/test/openapi31/openapi31.gen.go#L88

    In your case, you would have a "Cipher" type, that's a string type.

    We don't have a way right now to declare arbitrary global constants of a simple type.

  2. deitch commented on Sep 29, 2026

    @deitch
    ContributorAuthor

    I think the oneOf is a reasonable workaround. I will try to work with it.

    In terms of intent:

    We can't tell your intent from your spec.
    You're asking for a type named SomeConst which is a string and we provide it, and it has only one value, so we go through our standard enum generation

    That is exactly it. I may be (likely am) misreading how my described intent gets interpreted. I look at this:

    components:
      schemas:
        SomeConst:
          type: string
          const: "someconststringinput/fields/aes256"

    And I don't read it as "asking for a type named SomeConst which is a string". I read it as "SomeConst is a single thing, of type string with a constant value, so that is a straight-up const."

    But I do get that even though in this scenario there is only one possible value for SomeConst, and therefore my desire of:

    const SomeConst = "someconststringinput/fields/aes256"

    makes sense and is the only possible interpretation, the possibility for SomeConst to be both of these:

    components:
      schemas:
        SomeConst:
          type: string
          const: "someconststringinput/fields/aes256"
          # and
        OtherConst
            type: string
            oneOf:
               - title: FirstVal
                  const: "someconststringinput/fields/aes256"
               - title: SecondVal
                  const: "otherconststringinput/subdata/ed25519"

    requires that both SomeConst and OtherConst be the same type of "thing" which sometimes has a single possible value, and sometimes multiple values. The only thing that satisfies that is a typed value as type SomeConst string and type OtherConst string, and therefore each possible value of it is a go const that is of the declared type.

    Given the above (and assuming I understood correctly), maybe a better way of saying this is, if a schema is a const, and only a single one (not oneOf), then maybe there should be an option to collapse it down?

    In the meantime, documentation about this would be very helpful (and avoid more questions from the next one who hits this).

  3. mromaszewicz commented on Sep 30, 2026

    @mromaszewicz
    Member

    Right, we could detect the special case of a const schema with a single value turning into a global const, however, at this point, we've shipped what we've got and I'd be breaking existing users. We're always stuck between a rock and a hard place here, trying to put concepts from a dynamic typing system into a statically typed language :)

    Honestly, this problem is so complex, I sometimes wonder if people shouldn't just generate models with an LLM at this point.

  4. deitch commented on Sep 30, 2026

    @deitch
    ContributorAuthor

    Right, we could detect the special case of a const schema with a single value turning into a global const, however, at this point, we've shipped what we've got and I'd be breaking existing users.

    That is a strong point. I kind of realized it last night after I logged off (does that even exist, in the era of always-connected smartphones? 🤷‍♂️ ).

    In any case, the summary basically is:

    1. Every top-level object under scheme will be a type. It might be a struct or just a defined type for a string or int, etc.
    2. Every "item" under that will be a struct property: properties of an object are properties of the struct; properties of a const are individual consts of type the created struct.
    3. Names will be provided to those, either matching if explicitly provided, derived otherwise

    Just needs some examples to clarify. Is that more or less accurate?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions