Skip to content

Many updates, you will need to update your configuration

Compare
Choose a tag to compare
@deepmap-marcinr deepmap-marcinr released this 21 May 00:39

Many changes to code generation

We've had many big changes which affect code generation on the back burner, for fear of breaking code dependent on previous behavior, but this was holding back many fixes, so this release, in its default behavior, breaks generated code to be incompatible with existing code. However, we've been able to provide configuration options to disable this new behavior in all cases. So, with small changes to how oapi-codegen is invoked, you can continue to generate compatible code, however, you will not have these new changes, all of which fix bugs.

Configuration and flag changes

First, we are moving from flag based configuration of the tool, to using a configuration file. The configuration file is directly loaded into the Options struct used by the codegen package. A number of the former flags are ignored, and the former configuration file will fail to load with default settings.

To help you migrate your configuration options, we've provided several new flags:

  • --old-config-style: When set, we will correctly parse all flags and configuration files in the previous way. When it becomes inconvenient to maintain this flag, we will remove it.
  • --output-config: When set, oapi-codegen will output a new-style configuration file based from all flags and an existing configuration file. Combined with the previous flag, you can use the tool to update your configuration files.

If previously you were running a command like:

oapi-codegen --package=petshop --generate="types,client,server,spec" -alias-types petshop-expanded.yaml

You will now get an error:

error processing flags: flags --alias-types aren't supported in new config style, please use --old-style-config or update your configuration

By adding the --old-config-style, code will be generated:

oapi-codegen --old-config-style  --package=petshop --generate="types,client,server,spec" -alias-types petshop-expanded.yaml

But, we're moving away from flags, so you can generate a config file like so:

oapi-codegen --output-config --old-config-style  --package=petshop --generate="types,client,server,spec" -alias-types petshop-expanded.yaml

And you will see this output:

package: petshop
generate:
  echo-server: true
  client: true
  models: true
  embedded-spec: true

You can save that to a config file, and use that to configure oapi-codegen in the future.

oapi-codegen --config config.yaml petshop-expanded.yaml

Schema merging for allOf has been rewritten

The expanded petstore example contains an allOf schema which merges NewPet properties with an ID:

    Pet:
      allOf:
        - $ref: '#/components/schemas/NewPet'
        - required:
          - id
          properties:
            id:
              type: integer
              format: int64
              description: Unique id of the pet

In the past, we would generate this code:

// Pet defines model for Pet.
type Pet struct {
        // Embedded struct due to allOf(#/components/schemas/NewPet)
        NewPet `yaml:",inline"`
        // Embedded fields due to inline allOf schema
        // Unique id of the pet
        Id int64 `json:"id"`
}

With this release, we generate this:

// Pet defines model for Pet.
type Pet struct {
	// Unique id of the pet
	Id int64 `json:"id"`

	// Name of the pet
	Name string `json:"name"`

	// Type of the pet
	Tag *string `json:"tag,omitempty"`
}

In this new release, we merge the OpenAPI schemas before generating the Go object - which allows for modifiers like required to affect the type union. This makes the generated types closer to the intent of the OpenAPI specification.

Since this is a breaking change, we've provided a configuration file option to select the old behavior:

compatibility:
  old-merge-schemas: true

$ref prefers type aliasing

In prior releases, a $ref to another type would create a type declaration.

Say that we have this spec fragment:

components:
  schemas:
    Something:
        type: object
    SomethingElse:
        $ref: "#/components/schemas/Something"

Prior releases would generate:

type Something  interface{}

type SomethingElse Something

This release will generate:

type Something interface{}

type SomethingElse = Something

We will create type definitions for anything we create that needs its own named type, like objects and enums, but everything else will be a type alias where this is possible. We did this for two major reasons. The first is that type redefinitions remove methods, like those of the json.Unmarshaler interface, which breaks JSON marshaling. The second reason is that all these types make working with the generated code annoying due to many type conversions.

Since this is a breaking change, you can disable it:

compatibility:
  old-aliasing: true

The former --alias-types flag is now defunct, since it's effectively always on. it's no longer used.

Enum value name collision resolution

In prior releases, two enums of different types but with the same values would result in a type collision. This release adds new code which performs global enum value collision detection, and it prefixes the typename to the enum values to resolve this collision. For example, consider this schema:

components:
  schemas:
        Enum1:
      type: string
      enum: [One, Two, Three]
    Enum2:
      type: string
      enum: [ One, Two, Three ]

Previously, we would generate non-compiling code:

// Defines values for Enum1.
const (
	One   Enum1 = "One"
	Three Enum1 = "Three"
	Two   Enum1 = "Two"
)

// Defines values for Enum2.
const (
	One   Enum2 = "One"
	Three Enum2 = "Three"
	Two   Enum2 = "Two"
)

Now, we generate:

// Defines values for Enum1.
const (
	Enum1One   Enum1 = "One"
	Enum1Three Enum1 = "Three"
	Enum1Two   Enum1 = "Two"
)

// Defines values for Enum2.
const (
	Enum2One   Enum2 = "One"
	Enum2Three Enum2 = "Three"
	Enum2Two   Enum2 = "Two"
)

This conflict resolution will not do anything if there is no conflict, but when two types have conflicting typenames, then both will have their typename prefixed to the value types, so this change should not be breaking, because the affected code is already broken, and currently working code will not trigger the resolution.

However, if for some reason it does break existing specs, you can disable it:

compatibility:
  old-enum-conflicts: true

What's Changed

New Contributors

Full Changelog: v1.10.1...v1.11.0