Repository: deepmap/oapi-codegen (go)

Issue:
Schema Property with only additionalProperties schema defined
We encountered an issue for when a schema property is defined with only the **type** and **additionalProperties** with the schema defined under that key.

The API we're retrieving the OpenAPI swagger spec from does this to allow a map[string]struct with the struct being the **additionalProperties**. The example below shows the schema we're working with and the problem occurs with the **relationships** schema.

```yaml
    WritableCircuitTerminationRequest:
      type: object
      description: |-
        Base class to use for serializers based on OrganizationalModel or PrimaryModel.

        Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
      properties:
        circuit:
          type: string
          format: uuid
        site:
          type: string
          format: uuid
          nullable: true
        location:
          type: string
          format: uuid
          nullable: true
        provider_network:
          type: string
          format: uuid
          nullable: true
        description:
          type: string
          maxLength: 200
        custom_fields:
          type: object
          additionalProperties: {}
        relationships:
          type: object
          additionalProperties:
            type: object
            required:
              - id
              - url
              - name
              - type
            properties:
              id:
                type: string
                format: uuid
                readOnly: true
              url:
                type: string
                format: uri
                readOnly: true
              name:
                type: string
                readOnly: true
              type:
                type: string
                readOnly: true
                example: one-to-many
              source:
                type: object
                properties:
                  label:
                    type: string
                    readOnly: true
                  object_type:
                    type: string
                    readOnly: true
                    example: dcim.site
                  objects:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        url:
                          type: string
                          format: uri
                          readOnly: true
                        display:
                          type: string
                          readOnly: true
                      additionalProperties: true
              destination:
                type: object
                properties:
                  label:
                    type: string
                    readOnly: true
                  object_type:
                    type: string
                    readOnly: true
                    example: dcim.site
                  objects:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        url:
                          type: string
                          format: uri
                          readOnly: true
                        display:
                          type: string
                          readOnly: true
                      additionalProperties: true
              peer:
                type: object
                properties:
                  label:
                    type: string
                    readOnly: true
                  object_type:
                    type: string
                    readOnly: true
                    example: dcim.site
                  objects:
                    type: array
                    items:
                      type: object
                      properties:
                        id:
                          type: string
                          format: uuid
                        url:
                          type: string
                          format: uri
                          readOnly: true
                        display:
                          type: string
                          readOnly: true
                      additionalProperties: true
      required:
        - circuit
```

When generating the types for the schema, it implies it there should be a new type, but forgoes the creation of that type. Below is the code generated.

```go
type BulkWritableCircuitTerminationRequest struct {
	// Circuit Returns a nested representation of an object on read, but accepts either the nested representation or the
	// primary key value on write operations.
	Circuit         NestedCircuitRequest          `json:"circuit"`
	CustomFields    *map[string]interface{}       `json:"custom_fields,omitempty"`
	Description     *string                       `json:"description,omitempty"`
	Id              openapi_types.UUID            `json:"id"`
	Location        *NestedLocationRequest        `json:"location"`
	PortSpeed       *int                          `json:"port_speed"`
	PpInfo          *string                       `json:"pp_info,omitempty"`
	ProviderNetwork *NestedProviderNetworkRequest `json:"provider_network"`
	Relationships   *map[string]struct {
		Destination *struct {
			Label      *string                                                                         `json:"label,omitempty"`
			ObjectType *string                                                                         `json:"object_type,omitempty"`
			Objects    *[]BulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item `json:"objects,omitempty"`
		} `json:"destination,omitempty"`
		Id   *openapi_types.UUID `json:"id,omitempty"`
		Name *string             `json:"name,omitempty"`
		Peer *struct {
			Label      *string                                                                  `json:"label,omitempty"`
			ObjectType *string                                                                  `json:"object_type,omitempty"`
			Objects    *[]BulkWritableCircuitTerminationRequest_Relationships_Peer_Objects_Item `json:"objects,omitempty"`
		} `json:"peer,omitempty"`
		Source *struct {
			Label      *string                                                                    `json:"label,omitempty"`
			ObjectType *string                                                                    `json:"object_type,omitempty"`
			Objects    *[]BulkWritableCircuitTerminationRequest_Relationships_Source_Objects_Item `json:"objects,omitempty"`
		} `json:"source,omitempty"`
		Type *string `json:"type,omitempty"`
		Url  *string `json:"url,omitempty"`
	} `json:"relationships,omitempty"`
```

It shows that there should be three additional types created but never creates them:

- BulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item
- BulkWritableCircuitTerminationRequest_Relationships_Peer_Objects_Item
- BulkWritableCircuitTerminationRequest_Relationships_Source_Objects_Item

Pull request:
Fix AdditionalPropertiesType Propagation
Fixes #1016 

Updated code to find AdditionalTypes under AdditionalPropertiesType as well. AdditionalPropertiesType is just another Schema object that wasn't being searched before.

I hope this is the proper fix for this as I'm new to Go and this project, but this spot seems to make sense as it's keeping the same structure this library is currently storing additional types, but adds the additional spot when finding AdditionalTypes that isn't currently being searched.

Patch:
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index 7d9f2cc..ea9e008 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -53,6 +53,7 @@ var (
 	flagExcludeSchemas     string
 	flagResponseTypeSuffix string
 	flagAliasTypes         bool
+	flagInitalismOverrides bool
 )
 
 type configuration struct {
@@ -98,6 +99,7 @@ func main() {
 	flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation")
 	flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "the suffix used for responses types")
 	flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible")
+	flag.BoolVar(&flagInitalismOverrides, "initialism-overrides", false, "Use initialism overrides")
 
 	flag.Parse()
 
@@ -396,6 +398,8 @@ func updateConfigFromFlags(cfg *configuration) error {
 		cfg.OutputFile = flagOutputFile
 	}
 
+	cfg.OutputOptions.InitialismOverrides = flagInitalismOverrides
+
 	return nil
 }
 
diff --git a/examples/authenticated-api/echo/server/jwt_authenticator.go b/examples/authenticated-api/echo/server/jwt_authenticator.go
index 4d6b334..2c1699b 100644
--- a/examples/authenticated-api/echo/server/jwt_authenticator.go
+++ b/examples/authenticated-api/echo/server/jwt_authenticator.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"strings"
 
+	"github.com/deepmap/oapi-codegen/pkg/middleware"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/lestrrat-go/jwx/jwt"
 )
@@ -17,6 +18,8 @@ type JWSValidator interface {
 	ValidateJWS(jws string) (jwt.Token, error)
 }
 
+const JWTClaimsContextKey = "jwt_claims"
+
 var (
 	ErrNoAuthHeader      = errors.New("Authorization header is missing")
 	ErrInvalidAuthHeader = errors.New("Authorization header is malformed")
@@ -73,6 +76,12 @@ func Authenticate(v JWSValidator, ctx context.Context, input *openapi3filter.Aut
 	if err != nil {
 		return fmt.Errorf("token claims don't match: %w", err)
 	}
+
+	// Set the property on the echo context so the handler is able to
+	// access the claims data we generate in here.
+	eCtx := middleware.GetEchoContext(ctx)
+	eCtx.Set(JWTClaimsContextKey, token)
+
 	return nil
 }
 
diff --git a/examples/petstore-expanded/strict/api/petstore-server.gen.go b/examples/petstore-expanded/strict/api/petstore-server.gen.go
index cd9352a..822dee7 100644
--- a/examples/petstore-expanded/strict/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/strict/api/petstore-server.gen.go
@@ -408,9 +408,8 @@ type StrictServerInterface interface {
 	FindPetByID(ctx context.Context, request FindPetByIDRequestObject) (FindPetByIDResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/go.mod b/go.mod
index e3364ea..240683f 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,7 @@ module github.com/deepmap/oapi-codegen
 
 require (
 	github.com/apapsch/go-jsonmerge/v2 v2.0.0
-	github.com/getkin/kin-openapi v0.114.0
+	github.com/getkin/kin-openapi v0.116.0
 	github.com/gin-gonic/gin v1.9.0
 	github.com/go-chi/chi/v5 v5.0.8
 	github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219
@@ -12,8 +12,8 @@ require (
 	github.com/lestrrat-go/jwx v1.2.25
 	github.com/matryer/moq v0.3.1
 	github.com/stretchr/testify v1.8.2
-	golang.org/x/text v0.8.0
-	golang.org/x/tools v0.7.0
+	golang.org/x/text v0.9.0
+	golang.org/x/tools v0.8.0
 	gopkg.in/yaml.v2 v2.4.0
 )
 
@@ -58,9 +58,9 @@ require (
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
 	golang.org/x/crypto v0.6.0 // indirect
-	golang.org/x/mod v0.9.0 // indirect
-	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/sys v0.6.0 // indirect
+	golang.org/x/mod v0.10.0 // indirect
+	golang.org/x/net v0.9.0 // indirect
+	golang.org/x/sys v0.7.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
diff --git a/go.sum b/go.sum
index 5e5310b..f5edf9d 100644
--- a/go.sum
+++ b/go.sum
@@ -16,8 +16,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
-github.com/getkin/kin-openapi v0.114.0 h1:ar7QiJpDdlR+zSyPjrLf8mNnpoFP/lI90XcywMCFNe8=
-github.com/getkin/kin-openapi v0.114.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
+github.com/getkin/kin-openapi v0.116.0 h1:o986hwgMzR972JzOG5j6+WTwWqllZLs1EJKMKCivs2E=
+github.com/getkin/kin-openapi v0.116.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
 github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
@@ -147,11 +147,11 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu
 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
 golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -160,17 +160,17 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
-golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
+golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
+golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 956d589..70dca56 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -138,7 +138,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
-	ops, err := OperationDefinitions(spec)
+	ops, err := OperationDefinitions(spec, opts.OutputOptions.InitialismOverrides)
 	if err != nil {
 		return "", fmt.Errorf("error creating operation definitions: %w", err)
 	}
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index afa4fc5..8d7f2e6 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -84,9 +84,10 @@ type OutputOptions struct {
 	ExcludeTags   []string          `yaml:"exclude-tags,omitempty"`   // Exclude operations that have one of these tags. Ignored when empty.
 	UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files
 
-	ExcludeSchemas     []string `yaml:"exclude-schemas,omitempty"`      // Exclude from generation schemas with given names. Ignored when empty.
-	ResponseTypeSuffix string   `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
-	ClientTypeName     string   `yaml:"client-type-name,omitempty"`     // Override the default generated client type with the value
+	ExcludeSchemas      []string `yaml:"exclude-schemas,omitempty"`      // Exclude from generation schemas with given names. Ignored when empty.
+	ResponseTypeSuffix  string   `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
+	ClientTypeName      string   `yaml:"client-type-name,omitempty"`     // Override the default generated client type with the value
+	InitialismOverrides bool     `yaml:"initialism-overrides,omitempty"` // Whether to use the initialism overrides
 }
 
 // UpdateDefaults sets reasonable default values for unset fields in Configuration
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 28b991f..01666ff 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -293,7 +293,11 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini
 
 					var typeName string
 					switch {
-					case StringInArray(contentTypeName, contentTypesJSON):
+					// HAL+JSON:
+					case StringInArray(contentTypeName, contentTypesHalJSON):
+						typeName = fmt.Sprintf("HALJSON%s", ToCamelCase(responseName))
+					// JSON:
+					case StringInArray(contentTypeName, contentTypesJSON) || util.IsMediaTypeJson(contentTypeName):
 						typeName = fmt.Sprintf("JSON%s", ToCamelCase(responseName))
 					// YAML:
 					case StringInArray(contentTypeName, contentTypesYAML):
@@ -483,9 +487,16 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []
 }
 
 // OperationDefinitions returns all operations for a swagger definition.
-func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
+func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]OperationDefinition, error) {
 	var operations []OperationDefinition
 
+	var toCamelCaseFunc func(string) string
+	if initialismOverrides {
+		toCamelCaseFunc = ToCamelCaseWithInitialism
+	} else {
+		toCamelCaseFunc = ToCamelCase
+	}
+
 	for _, requestPath := range SortedPathsKeys(swagger.Paths) {
 		pathItem := swagger.Paths[requestPath]
 		// These are parameters defined for all methods on a given path. They
@@ -505,13 +516,13 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 			}
 			// We rely on OperationID to generate function names, it's required
 			if op.OperationID == "" {
-				op.OperationID, err = generateDefaultOperationID(opName, requestPath)
+				op.OperationID, err = generateDefaultOperationID(opName, requestPath, toCamelCaseFunc)
 				if err != nil {
 					return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s",
 						opName, requestPath, err)
 				}
 			} else {
-				op.OperationID = ToCamelCase(op.OperationID)
+				op.OperationID = toCamelCaseFunc(op.OperationID)
 			}
 			op.OperationID = typeNamePrefix(op.OperationID) + op.OperationID
 
@@ -550,7 +561,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 				HeaderParams: FilterParameterDefinitionByType(allParams, "header"),
 				QueryParams:  FilterParameterDefinitionByType(allParams, "query"),
 				CookieParams: FilterParameterDefinitionByType(allParams, "cookie"),
-				OperationId:  ToCamelCase(op.OperationID),
+				OperationId:  toCamelCaseFunc(op.OperationID),
 				// Replace newlines in summary.
 				Summary:         op.Summary,
 				Method:          opName,
@@ -588,7 +599,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 	return operations, nil
 }
 
-func generateDefaultOperationID(opName string, requestPath string) (string, error) {
+func generateDefaultOperationID(opName string, requestPath string, toCamelCaseFunc func(string) string) (string, error) {
 	var operationId = strings.ToLower(opName)
 
 	if opName == "" {
@@ -605,7 +616,7 @@ func generateDefaultOperationID(opName string, requestPath string) (string, erro
 		}
 	}
 
-	return ToCamelCase(operationId), nil
+	return toCamelCaseFunc(operationId), nil
 }
 
 // GenerateBodyDefinitions turns the Swagger body definitions into a list of our body
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index 457e102..92f72b4 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -71,6 +71,11 @@ func (s Schema) GetAdditionalTypeDefs() []TypeDefinition {
 	for _, p := range s.Properties {
 		result = append(result, p.Schema.GetAdditionalTypeDefs()...)
 	}
+	// Some schema definitions may be used for key/value and not specify any properties, but provide properties
+	// within the additionalProperties. See test_schema_array_types.yaml in tests.
+	if s.AdditionalPropertiesType != nil {
+		result = append(result, s.AdditionalPropertiesType.GetAdditionalTypeDefs()...)
+	}
 	result = append(result, s.AdditionalTypes...)
 	return result
 }
@@ -638,17 +643,20 @@ func GenFieldsFromProperties(props []Property) []string {
 
 		field += fmt.Sprintf("    %s %s", goFieldName, p.GoTypeDef())
 
+		omitEmpty := !p.Nullable &&
+			(!p.Required || p.ReadOnly || p.WriteOnly) &&
+			(!p.Required || !p.ReadOnly || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer)
+
 		// Support x-omitempty
-		overrideOmitEmpty := true
-		if _, ok := p.Extensions[extPropOmitEmpty]; ok {
-			if extOmitEmpty, err := extParseOmitEmpty(p.Extensions[extPropOmitEmpty]); err == nil {
-				overrideOmitEmpty = extOmitEmpty
+		if extOmitEmptyValue, ok := p.Extensions[extPropOmitEmpty]; ok {
+			if extOmitEmpty, err := extParseOmitEmpty(extOmitEmptyValue); err == nil {
+				omitEmpty = extOmitEmpty
 			}
 		}
 
 		fieldTags := make(map[string]string)
 
-		if (p.Required && !p.ReadOnly && !p.WriteOnly) || p.Nullable || !overrideOmitEmpty || (p.Required && p.ReadOnly && globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer) {
+		if !omitEmpty {
 			fieldTags["json"] = p.JsonFieldName
 			if p.NeedsFormTag {
 				fieldTags["form"] = p.JsonFieldName
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index 16671fb..bae1e30 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -34,9 +34,10 @@ const (
 )
 
 var (
-	contentTypesJSON = []string{echo.MIMEApplicationJSON, "text/x-json", "application/problem+json"}
-	contentTypesYAML = []string{"application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"}
-	contentTypesXML  = []string{echo.MIMEApplicationXML, echo.MIMETextXML, "application/problems+xml"}
+	contentTypesJSON    = []string{echo.MIMEApplicationJSON, "text/x-json", "application/problem+json"}
+	contentTypesHalJSON = []string{"application/hal+json"}
+	contentTypesYAML    = []string{"application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"}
+	contentTypesXML     = []string{echo.MIMEApplicationXML, echo.MIMETextXML, "application/problems+xml"}
 
 	responseTypeSuffix = "Response"
 
diff --git a/pkg/codegen/templates/strict/strict-echo.tmpl b/pkg/codegen/templates/strict/strict-echo.tmpl
index 4bd6aec..86def37 100644
--- a/pkg/codegen/templates/strict/strict-echo.tmpl
+++ b/pkg/codegen/templates/strict/strict-echo.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx echo.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictEchoHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictEchoMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
     return &strictHandler{ssi: ssi, middlewares: middlewares}
diff --git a/pkg/codegen/templates/strict/strict-gin.tmpl b/pkg/codegen/templates/strict/strict-gin.tmpl
index 9622fb2..0c36f40 100644
--- a/pkg/codegen/templates/strict/strict-gin.tmpl
+++ b/pkg/codegen/templates/strict/strict-gin.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx *gin.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictGinHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictGinMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
     return &strictHandler{ssi: ssi, middlewares: middlewares}
@@ -83,6 +82,7 @@ type strictHandler struct {
 
         if err != nil {
             ctx.Error(err)
+            ctx.Status(http.StatusInternalServerError)
         } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok {
             if err := validResponse.Visit{{$opid}}Response(ctx.Writer); err != nil {
                 ctx.Error(err)
diff --git a/pkg/codegen/templates/strict/strict-http.tmpl b/pkg/codegen/templates/strict/strict-http.tmpl
index d04503d..b16ed1d 100644
--- a/pkg/codegen/templates/strict/strict-http.tmpl
+++ b/pkg/codegen/templates/strict/strict-http.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
     RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index c386cd3..3922538 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -17,6 +17,7 @@ import (
 	"fmt"
 	"go/token"
 	"net/url"
+	"reflect"
 	"regexp"
 	"sort"
 	"strconv"
@@ -160,6 +161,24 @@ func ToCamelCase(str string) string {
 	return n
 }
 
+func ToCamelCaseWithInitialism(str string) string {
+	return replaceInitialism(ToCamelCase(str))
+}
+
+func replaceInitialism(s string) string {
+	// These strings do not apply CamelCase
+	// Do not do CamelCase when these characters match when the preceding character is lowercase
+	// ["Acl", "Api", "Ascii", "Cpu", "Css", "Dns", "Eof", "Guid", "Html", "Http", "Https", "Id", "Ip", "Json", "Qps", "Ram", "Rpc", "Sla", "Smtp", "Sql", "Ssh", "Tcp", "Tls", "Ttl", "Udp", "Ui", "Gid", "Uid", "Uuid", "Uri", "Url", "Utf8", "Vm", "Xml", "Xmpp", "Xsrf", "Xss", "Sip", "Rtp", "Amqp", "Db", "Ts"]
+	targetWordRegex := regexp.MustCompile(`(?i)(Acl|Api|Ascii|Cpu|Css|Dns|Eof|Guid|Html|Http|Https|Id|Ip|Json|Qps|Ram|Rpc|Sla|Smtp|Sql|Ssh|Tcp|Tls|Ttl|Udp|Ui|Gid|Uid|Uuid|Uri|Url|Utf8|Vm|Xml|Xmpp|Xsrf|Xss|Sip|Rtp|Amqp|Db|Ts)`)
+	return targetWordRegex.ReplaceAllStringFunc(s, func(s string) string {
+		// If the preceding character is lowercase, do not do CamelCase
+		if unicode.IsLower(rune(s[0])) {
+			return s
+		}
+		return strings.ToUpper(s)
+	})
+}
+
 // SortedSchemaKeys returns the keys of the given SchemaRef dictionary in sorted
 // order, since Golang scrambles dictionary keys
 func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string {
@@ -904,5 +923,5 @@ func TypeDefinitionsEquivalent(t1, t2 TypeDefinition) bool {
 	if t1.TypeName != t2.TypeName {
 		return false
 	}
-	return t1.Schema.OAPISchema == t2.Schema.OAPISchema
+	return reflect.DeepEqual(t1.Schema.OAPISchema, t2.Schema.OAPISchema)
 }
diff --git a/pkg/runtime/strictmiddleware.go b/pkg/runtime/strictmiddleware.go
new file mode 100644
index 0000000..fac240e
--- /dev/null
+++ b/pkg/runtime/strictmiddleware.go
@@ -0,0 +1,21 @@
+package runtime
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/labstack/echo/v4"
+)
+
+type StrictEchoHandlerFunc func(ctx echo.Context, request interface{}) (response interface{}, err error)
+
+type StrictEchoMiddlewareFunc func(f StrictEchoHandlerFunc, operationID string) StrictEchoHandlerFunc
+
+type StrictHttpHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (response interface{}, err error)
+
+type StrictHttpMiddlewareFunc func(f StrictHttpHandlerFunc, operationID string) StrictHttpHandlerFunc
+
+type StrictGinHandlerFunc func(ctx *gin.Context, request interface{}) (response interface{}, err error)
+
+type StrictGinMiddlewareFunc func(f StrictGinHandlerFunc, operationID string) StrictGinHandlerFunc


Test patch:
diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
index 09e8df3..69766e1 100644
--- a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
+++ b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	externalRef0 "github.com/deepmap/oapi-codegen/internal/test/issues/issue-removed-external-ref/gen/spec_ext"
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/go-chi/chi/v5"
 )
 
@@ -244,9 +245,8 @@ type StrictServerInterface interface {
 	PostNoTrouble(ctx context.Context, request PostNoTroubleRequestObject) (PostNoTroubleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
index 21222f8..ed5a474 100644
--- a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
+++ b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
@@ -4,10 +4,10 @@
 package spec_ext
 
 import (
-	"context"
 	"fmt"
 	"net/http"
 
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/go-chi/chi/v5"
 )
 
@@ -161,9 +161,8 @@ type PascalJSONResponse PascalSchema
 type StrictServerInterface interface {
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go
index 236277c..b3904b5 100644
--- a/internal/test/strict-server/chi/server.gen.go
+++ b/internal/test/strict-server/chi/server.gen.go
@@ -892,9 +892,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/strict-server/echo/server.gen.go b/internal/test/strict-server/echo/server.gen.go
index ebd374b..0912230 100644
--- a/internal/test/strict-server/echo/server.gen.go
+++ b/internal/test/strict-server/echo/server.gen.go
@@ -706,9 +706,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx echo.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictEchoHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictEchoMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
 	return &strictHandler{ssi: ssi, middlewares: middlewares}
diff --git a/internal/test/strict-server/gin/server.gen.go b/internal/test/strict-server/gin/server.gen.go
index 618db70..dcac415 100644
--- a/internal/test/strict-server/gin/server.gen.go
+++ b/internal/test/strict-server/gin/server.gen.go
@@ -763,9 +763,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx *gin.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictGinHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictGinMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
 	return &strictHandler{ssi: ssi, middlewares: middlewares}
@@ -799,6 +798,7 @@ func (sh *strictHandler) JSONExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(JSONExampleResponseObject); ok {
 		if err := validResponse.VisitJSONExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -830,6 +830,7 @@ func (sh *strictHandler) MultipartExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(MultipartExampleResponseObject); ok {
 		if err := validResponse.VisitMultipartExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -896,6 +897,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok {
 		if err := validResponse.VisitMultipleRequestAndResponseTypesResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -922,6 +924,7 @@ func (sh *strictHandler) ReservedGoKeywordParameters(ctx *gin.Context, pType str
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok {
 		if err := validResponse.VisitReservedGoKeywordParametersResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -954,6 +957,7 @@ func (sh *strictHandler) ReusableResponses(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(ReusableResponsesResponseObject); ok {
 		if err := validResponse.VisitReusableResponsesResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -986,6 +990,7 @@ func (sh *strictHandler) TextExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(TextExampleResponseObject); ok {
 		if err := validResponse.VisitTextExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1012,6 +1017,7 @@ func (sh *strictHandler) UnknownExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(UnknownExampleResponseObject); ok {
 		if err := validResponse.VisitUnknownExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1040,6 +1046,7 @@ func (sh *strictHandler) UnspecifiedContentType(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok {
 		if err := validResponse.VisitUnspecifiedContentTypeResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1075,6 +1082,7 @@ func (sh *strictHandler) URLEncodedExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok {
 		if err := validResponse.VisitURLEncodedExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1109,6 +1117,7 @@ func (sh *strictHandler) HeadersExample(ctx *gin.Context, params HeadersExampleP
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(HeadersExampleResponseObject); ok {
 		if err := validResponse.VisitHeadersExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go
index 6ae6121..499174b 100644
--- a/pkg/codegen/codegen_test.go
+++ b/pkg/codegen/codegen_test.go
@@ -201,6 +201,49 @@ type GetTestByNameResponse struct {
 	checkLint(t, "test.gen.go", []byte(code))
 }
 
+// Validate any types nested under AdditionalPropertiesTypes is propagated up as with normal Property AdditionalTypes
+func TestExampleOpenAPICodeGenerationSchemaAdditionalPropertyTypes(t *testing.T) {
+
+	// Input vars for code generation:
+	packageName := "testswagger"
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			EchoServer:   false,
+			Client:       false,
+			Models:       true,
+			EmbeddedSpec: false,
+		},
+	}
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+
+	// Get a spec from the test definition in this file:
+	swagger, err := loader.LoadFromData([]byte(testSchemaArrayTypes))
+	assert.NoError(t, err)
+
+	// Run our code generation:
+	code, err := Generate(swagger, opts)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, code)
+
+	// Check that we have valid (formattable) code:
+	_, err = format.Source([]byte(code))
+	assert.NoError(t, err)
+
+	// Check that we have a package:
+	assert.Contains(t, code, "package testswagger")
+
+	// Check that AdditionalPropertyTypes structs are propagated up properly:
+	assert.Contains(t, code, "[]PatchedBulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item")
+	assert.Contains(t, code, "type PatchedBulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item struct {")
+	assert.Contains(t, code, "[]BulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item")
+	assert.Contains(t, code, "type BulkWritableCircuitTerminationRequest_Relationships_Destination_Objects_Item struct {")
+	assert.Contains(t, code, "[]WritableCircuitTerminationRequest_Relationships_Destination_Objects_Item ")
+	assert.Contains(t, code, "type WritableCircuitTerminationRequest_Relationships_Destination_Objects_Item struct {")
+}
+
 func TestGoTypeImport(t *testing.T) {
 	packageName := "api"
 	opts := Configuration{
@@ -309,5 +352,8 @@ func (t *ExampleSchema_Item) FromExternalRef0NewPet(v externalRef0.NewPet) error
 
 }
 
+//go:embed test_schema_additional_properties_types.yaml
+var testSchemaArrayTypes string
+
 //go:embed test_spec.yaml
 var testOpenAPIDefinition string
diff --git a/pkg/codegen/operations_test.go b/pkg/codegen/operations_test.go
index 0eff03e..77a54a8 100644
--- a/pkg/codegen/operations_test.go
+++ b/pkg/codegen/operations_test.go
@@ -131,7 +131,7 @@ func TestGenerateDefaultOperationID(t *testing.T) {
 	}
 
 	for _, test := range suite {
-		got, err := generateDefaultOperationID(test.op, test.path)
+		got, err := generateDefaultOperationID(test.op, test.path, ToCamelCase)
 		if err != nil {
 			if !test.wantErr {
 				t.Fatalf("did not expected error but got %v", err)
diff --git a/pkg/codegen/test_schema_additional_properties_types.yaml b/pkg/codegen/test_schema_additional_properties_types.yaml
new file mode 100644
index 0000000..0cbf800
--- /dev/null
+++ b/pkg/codegen/test_schema_additional_properties_types.yaml
@@ -0,0 +1,513 @@
+openapi: 3.0.3
+info:
+  title: API Documentation
+  version: 1.5.7 (1.3)
+  description: Source of truth and network automation platform
+  license:
+    name: Apache v2 License
+paths:
+  /circuits/circuit-terminations/:
+    post:
+      operationId: circuits_circuit_terminations_create
+      description: |-
+        Base class to use for API ViewSets based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support Notes.
+      tags:
+        - circuits
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: "#/components/schemas/WritableCircuitTerminationRequest"
+        required: true
+      security:
+        - cookieAuth: []
+        - tokenAuth: []
+      responses:
+        "201":
+          content:
+            application/json; version=1.3:
+              schema:
+                $ref: "#/components/schemas/CircuitTermination"
+          description: ""
+    put:
+      operationId: circuits_circuit_terminations_bulk_update
+      description: |-
+        Base class to use for API ViewSets based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support Notes.
+      tags:
+        - circuits
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: array
+              items:
+                $ref: "#/components/schemas/BulkWritableCircuitTerminationRequest"
+        required: true
+      security:
+        - cookieAuth: []
+        - tokenAuth: []
+      responses:
+        "200":
+          content:
+            application/json; version=1.3:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/CircuitTermination"
+          description: ""
+    patch:
+      operationId: circuits_circuit_terminations_bulk_partial_update
+      description: |-
+        Base class to use for API ViewSets based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support Notes.
+      tags:
+        - circuits
+      requestBody:
+        content:
+          application/json:
+            schema:
+              type: array
+              items:
+                $ref: "#/components/schemas/PatchedBulkWritableCircuitTerminationRequest"
+        required: true
+      security:
+        - cookieAuth: []
+        - tokenAuth: []
+      responses:
+        "200":
+          content:
+            application/json; version=1.3:
+              schema:
+                type: array
+                items:
+                  $ref: "#/components/schemas/CircuitTermination"
+          description: ""
+components:
+  schemas:
+    CircuitTermination:
+      type: object
+      description: |-
+        Base class to use for serializers based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
+      properties:
+        id:
+          type: string
+          format: uuid
+          readOnly: true
+        display:
+          type: string
+          readOnly: true
+          description: Human friendly display value
+        url:
+          type: string
+          format: uri
+          readOnly: true
+        description:
+          type: string
+          maxLength: 200
+      required:
+        - display
+        - id
+        - url
+    WritableCircuitTerminationRequest:
+      type: object
+      description: |-
+        Base class to use for serializers based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
+      properties:
+        circuit:
+          type: string
+          format: uuid
+        site:
+          type: string
+          format: uuid
+          nullable: true
+        location:
+          type: string
+          format: uuid
+          nullable: true
+        provider_network:
+          type: string
+          format: uuid
+          nullable: true
+        description:
+          type: string
+          maxLength: 200
+        custom_fields:
+          type: object
+          additionalProperties: {}
+        relationships:
+          type: object
+          additionalProperties:
+            type: object
+            required:
+              - id
+              - url
+              - name
+              - type
+            properties:
+              id:
+                type: string
+                format: uuid
+                readOnly: true
+              url:
+                type: string
+                format: uri
+                readOnly: true
+              name:
+                type: string
+                readOnly: true
+              type:
+                type: string
+                readOnly: true
+                example: one-to-many
+              source:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              destination:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              peer:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+      required:
+        - circuit
+    BulkWritableCircuitTerminationRequest:
+      type: object
+      description: |-
+        Base class to use for serializers based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
+      properties:
+        id:
+          type: string
+          format: uuid
+        description:
+          type: string
+          maxLength: 200
+        custom_fields:
+          type: object
+          additionalProperties: true
+        relationships:
+          type: object
+          additionalProperties:
+            type: object
+            required:
+              - id
+              - url
+              - name
+              - type
+            properties:
+              id:
+                type: string
+                format: uuid
+                readOnly: true
+              url:
+                type: string
+                format: uri
+                readOnly: true
+              name:
+                type: string
+                readOnly: true
+              type:
+                type: string
+                readOnly: true
+                example: one-to-many
+              source:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              destination:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              peer:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+      required:
+        - id
+    PatchedBulkWritableCircuitTerminationRequest:
+      type: object
+      description: |-
+        Base class to use for serializers based on OrganizationalModel or PrimaryModel.
+
+        Can also be used for models derived from BaseModel, so long as they support custom fields and relationships.
+      properties:
+        id:
+          type: string
+          format: uuid
+        port_speed:
+          type: integer
+          maximum: 2147483647
+          minimum: 0
+          nullable: true
+          title: Port speed (Kbps)
+        upstream_speed:
+          type: integer
+          maximum: 2147483647
+          minimum: 0
+          nullable: true
+          title: Upstream speed (Kbps)
+          description: Upstream speed, if different from port speed
+        xconnect_id:
+          type: string
+          title: Cross-connect ID
+          maxLength: 50
+        pp_info:
+          type: string
+          title: Patch panel/port(s)
+          maxLength: 100
+        description:
+          type: string
+          maxLength: 200
+        custom_fields:
+          type: object
+          additionalProperties: {}
+        relationships:
+          type: object
+          additionalProperties:
+            type: object
+            required:
+              - id
+              - url
+              - name
+              - type
+            properties:
+              id:
+                type: string
+                format: uuid
+                readOnly: true
+              url:
+                type: string
+                format: uri
+                readOnly: true
+              name:
+                type: string
+                readOnly: true
+              type:
+                type: string
+                readOnly: true
+                example: one-to-many
+              source:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              destination:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+              peer:
+                type: object
+                properties:
+                  label:
+                    type: string
+                    readOnly: true
+                  object_type:
+                    type: string
+                    readOnly: true
+                    example: dcim.site
+                  objects:
+                    type: array
+                    items:
+                      type: object
+                      properties:
+                        id:
+                          type: string
+                          format: uuid
+                        url:
+                          type: string
+                          format: uri
+                          readOnly: true
+                        display:
+                          type: string
+                          readOnly: true
+                      additionalProperties: true
+      required:
+        - id
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index 32708f0..644167a 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -433,6 +433,18 @@ func TestSchemaNameToTypeName(t *testing.T) {
 	}
 }
 
+
+func TestTypeDefinitionsEquivalent(t *testing.T) {
+	def1 := TypeDefinition{TypeName: "name", Schema: Schema{
+		OAPISchema: &openapi3.Schema{},
+	}}
+	def2 := TypeDefinition{TypeName: "name", Schema: Schema{
+		OAPISchema: &openapi3.Schema{},
+	}}
+	assert.True(t, TypeDefinitionsEquivalent(def1, def2))
+}
+
+
 func TestRefPathToObjName(t *testing.T) {
 	t.Parallel()
 
@@ -446,3 +458,45 @@ func TestRefPathToObjName(t *testing.T) {
 		assert.Equal(t, want, RefPathToObjName(in))
 	}
 }
+
+func Test_replaceInitialisms(t *testing.T) {
+	type args struct {
+		s string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "empty string",
+			args: args{s: ""},
+			want: "",
+		},
+		{
+			name: "no initialism",
+			args: args{s: "foo"},
+			want: "foo",
+		},
+		{
+			name: "one initialism",
+			args: args{s: "fooId"},
+			want: "fooID",
+		},
+		{
+			name: "two initialism",
+			args: args{s: "fooIdBarApi"},
+			want: "fooIDBarAPI",
+		},
+		{
+			name: "already initialism",
+			args: args{s: "fooIDBarAPI"},
+			want: "fooIDBarAPI",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert.Equalf(t, tt.want, replaceInitialism(tt.args.s), "replaceInitialism(%v)", tt.args.s)
+		})
+	}
+}