Repository: deepmap/oapi-codegen
Pull request #194 (2020-06-17 09:31:01)

Fix codegen not including additionalProperties in allOf schema
this draft PR attempts to fix issue: https://github.com/deepmap/oapi-codegen/issues/193

tested locally and works.  But there might be a more elegant way to fix this 😄 


Patch:
diff --git a/README.md b/README.md
index 6f7d9ae..1d5d39a 100644
--- a/README.md
+++ b/README.md
@@ -440,6 +440,8 @@ you can specify any combination of those.
  the generated file in case the spec contains weird strings.
 - `skip-prune`: skip pruning unused components from the spec prior to generating
  the code.
+- `import-mapping`: specifies a map of references external OpenAPI specs to go
+ Go include paths. Please see below.
 
 So, for example, if you would like to produce only the server code, you could
 run `oapi-generate -generate types,server`. You could generate `types` and
@@ -453,6 +455,28 @@ To generate a server that only handles `admin` paths, use the argument
 `-include-tags="admin"`. When neither of these arguments is present, all paths
 are generated.
 
+### Import Mappings
+
+OpenAPI specifications may contain references to other OpenAPI specifications,
+and we need some additional information in order to be able to generate correct
+Go code.
+
+An external reference looks like this:
+
+    $ref: ./some_spec.yaml#/components/schemas/Type
+    
+We assume that you have already generated the boilerplate code for `./some_spec.yaml`
+using `oapi-codegen`, and you have a package which contains the generated code,
+let's call it `github.com/deepmap/some-package`. You need to tell `oapi-codegen` that
+`some_spec.yaml` corresponds to this package, and you would do it by specifying
+this command line argument:
+
+    -import-mapping=./some_spec.yaml:github.com/deepmap/some-package
+    
+This tells us that in order to resolve references generated from `some_spec.yaml` we
+need to import `github.com/deepmap/some-package`. You may specify multiple mappings
+by comma separating them in the form `key1:value1,key2:value2`.
+    
 ## What's missing or incomplete
 
 This code is still young, and not complete, since we're filling it in as we
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index b578e9f..6a6b523 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -33,12 +33,13 @@ func errExit(format string, args ...interface{}) {
 
 func main() {
 	var (
-		packageName  string
-		generate     string
-		outputFile   string
-		includeTags  string
-		excludeTags  string
-		templatesDir string
+		packageName   string
+		generate      string
+		outputFile    string
+		includeTags   string
+		excludeTags   string
+		templatesDir  string
+		importMapping string
 	)
 	flag.StringVar(&packageName, "package", "", "The package name for generated code")
 	flag.StringVar(&generate, "generate", "types,client,server,spec",
@@ -47,6 +48,7 @@ func main() {
 	flag.StringVar(&includeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
 	flag.StringVar(&excludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
 	flag.StringVar(&templatesDir, "templates", "", "Path to directory containing user templates")
+	flag.StringVar(&importMapping, "import-mapping", "", "A dict from the external reference to golang package path")
 	flag.Parse()
 
 	if flag.NArg() < 1 {
@@ -106,6 +108,13 @@ func main() {
 	}
 	opts.UserTemplates = templates
 
+	if len(importMapping) > 0 {
+		opts.ImportMapping, err = util.ParseCommandlineMap(importMapping)
+		if err != nil {
+			errExit("error parsing import-mapping: %s\n", err)
+		}
+	}
+
 	code, err := codegen.Generate(swagger, packageName, opts)
 	if err != nil {
 		errExit("error generating code: %s\n", err)
@@ -159,3 +168,4 @@ func loadTemplateOverrides(templatesDir string) (map[string]string, error) {
 
 	return templates, nil
 }
+
diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go
index d3c25c0..859e2db 100644
--- a/examples/petstore-expanded/chi/api/petstore.gen.go
+++ b/examples/petstore-expanded/chi/api/petstore.gen.go
@@ -6,7 +6,6 @@ package api
 import (
 	"bytes"
 	"compress/gzip"
-	"context"
 	"encoding/base64"
 	"fmt"
 	"github.com/deepmap/oapi-codegen/pkg/runtime"
@@ -62,111 +61,102 @@ type AddPetJSONBody NewPet
 // AddPetRequestBody defines body for AddPet for application/json ContentType.
 type AddPetJSONRequestBody AddPetJSONBody
 
+// ServerInterface represents all server handlers.
 type ServerInterface interface {
-	// Returns all pets (GET /pets)
-	FindPets(w http.ResponseWriter, r *http.Request)
-	// Creates a new pet (POST /pets)
+	// Returns all pets
+	// (GET /pets)
+	FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams)
+	// Creates a new pet
+	// (POST /pets)
 	AddPet(w http.ResponseWriter, r *http.Request)
-	// Deletes a pet by ID (DELETE /pets/{id})
-	DeletePet(w http.ResponseWriter, r *http.Request)
-	// Returns a pet by ID (GET /pets/{id})
-	FindPetById(w http.ResponseWriter, r *http.Request)
+	// Deletes a pet by ID
+	// (DELETE /pets/{id})
+	DeletePet(w http.ResponseWriter, r *http.Request, id int64)
+	// Returns a pet by ID
+	// (GET /pets/{id})
+	FindPetById(w http.ResponseWriter, r *http.Request, id int64)
 }
 
-// ParamsForFindPets operation parameters from context
-func ParamsForFindPets(ctx context.Context) *FindPetsParams {
-	return ctx.Value("FindPetsParams").(*FindPetsParams)
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
 }
 
 // FindPets operation middleware
-func FindPetsCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) FindPets(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// Parameter object where we will unmarshal all parameters from the context
-		var params FindPetsParams
+	// Parameter object where we will unmarshal all parameters from the context
+	var params FindPetsParams
 
-		// ------------- Optional query parameter "tags" -------------
-		if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
+	// ------------- Optional query parameter "tags" -------------
+	if paramValue := r.URL.Query().Get("tags"); paramValue != "" {
 
-		}
-
-		err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter tags: %s", err), http.StatusBadRequest)
-			return
-		}
+	}
 
-		// ------------- Optional query parameter "limit" -------------
-		if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
+	err = runtime.BindQueryParameter("form", true, false, "tags", r.URL.Query(), &params.Tags)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter tags: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		}
+	// ------------- Optional query parameter "limit" -------------
+	if paramValue := r.URL.Query().Get("limit"); paramValue != "" {
 
-		err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter limit: %s", err), http.StatusBadRequest)
-			return
-		}
+	}
 
-		ctx = context.WithValue(ctx, "FindPetsParams", &params)
+	err = runtime.BindQueryParameter("form", true, false, "limit", r.URL.Query(), &params.Limit)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter limit: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.FindPets(w, r.WithContext(ctx), params)
 }
 
 // AddPet operation middleware
-func AddPetCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) AddPet(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.AddPet(w, r.WithContext(ctx))
 }
 
 // DeletePet operation middleware
-func DeletePetCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
-
-		var err error
+func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		// ------------- Path parameter "id" -------------
-		var id int64
+	var err error
 
-		err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
-			return
-		}
+	// ------------- Path parameter "id" -------------
+	var id int64
 
-		ctx = context.WithValue(ctx, "id", id)
+	err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.DeletePet(w, r.WithContext(ctx), id)
 }
 
 // FindPetById operation middleware
-func FindPetByIdCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) FindPetById(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// ------------- Path parameter "id" -------------
-		var id int64
+	// ------------- Path parameter "id" -------------
+	var id int64
 
-		err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
-			return
-		}
-
-		ctx = context.WithValue(ctx, "id", id)
+	err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.FindPetById(w, r.WithContext(ctx), id)
 }
 
 // Handler creates http.Handler with routing matching OpenAPI spec.
@@ -176,21 +166,21 @@ func Handler(si ServerInterface) http.Handler {
 
 // HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
 func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
 	r.Group(func(r chi.Router) {
-		r.Use(FindPetsCtx)
-		r.Get("/pets", si.FindPets)
+		r.Get("/pets", wrapper.FindPets)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(AddPetCtx)
-		r.Post("/pets", si.AddPet)
+		r.Post("/pets", wrapper.AddPet)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(DeletePetCtx)
-		r.Delete("/pets/{id}", si.DeletePet)
+		r.Delete("/pets/{id}", wrapper.DeletePet)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(FindPetByIdCtx)
-		r.Get("/pets/{id}", si.FindPetById)
+		r.Get("/pets/{id}", wrapper.FindPetById)
 	})
 
 	return r
diff --git a/examples/petstore-expanded/chi/api/petstore.go b/examples/petstore-expanded/chi/api/petstore.go
index a7e2358..a8d31d7 100644
--- a/examples/petstore-expanded/chi/api/petstore.go
+++ b/examples/petstore-expanded/chi/api/petstore.go
@@ -34,9 +34,7 @@ func sendPetstoreError(w http.ResponseWriter, code int, message string) {
 }
 
 // Here, we implement all of the handlers in the ServerInterface
-func (p *PetStore) FindPets(w http.ResponseWriter, r *http.Request) {
-	params := ParamsForFindPets(r.Context())
-
+func (p *PetStore) FindPets(w http.ResponseWriter, r *http.Request, params FindPetsParams) {
 	p.Lock.Lock()
 	defer p.Lock.Unlock()
 
@@ -97,9 +95,7 @@ func (p *PetStore) AddPet(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(pet)
 }
 
-func (p *PetStore) FindPetById(w http.ResponseWriter, r *http.Request) {
-	id := r.Context().Value("id").(int64)
-
+func (p *PetStore) FindPetById(w http.ResponseWriter, r *http.Request, id int64) {
 	p.Lock.Lock()
 	defer p.Lock.Unlock()
 
@@ -113,9 +109,7 @@ func (p *PetStore) FindPetById(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(pet)
 }
 
-func (p *PetStore) DeletePet(w http.ResponseWriter, r *http.Request) {
-	id := r.Context().Value("id").(int64)
-
+func (p *PetStore) DeletePet(w http.ResponseWriter, r *http.Request, id int64) {
 	p.Lock.Lock()
 	defer p.Lock.Unlock()
 
diff --git a/go.sum b/go.sum
index d66a76f..1fb5ee4 100644
--- a/go.sum
+++ b/go.sum
@@ -6,8 +6,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/getkin/kin-openapi v0.3.0 h1:xsQ4mA20YJDMgIHdHqMKZ66QUe6/hi+x6yLsTTz8xyQ=
-github.com/getkin/kin-openapi v0.3.0/go.mod h1:W8dhxZgpE84ciM+VIItFqkmZ4eHtuomrdIHtASQIqi0=
 github.com/getkin/kin-openapi v0.13.0 h1:03fqBEEgivp4MVK2ElB140B56hjO9ZFvFTHBsvFsSro=
 github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
 github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
@@ -43,8 +41,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
@@ -89,7 +85,5 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 9f12de5..ea8cf3b 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -42,6 +42,7 @@ type Options struct {
 	IncludeTags        []string          // Only include operations that have one of these tags. Ignored when empty.
 	ExcludeTags        []string          // Exclude operations that have one of these tags. Ignored when empty.
 	UserTemplates      map[string]string // Override built-in templates from user-provided files
+	ImportMapping      map[string]string // ImportMapping specifies the golang package path for each external reference
 }
 
 type goImport struct {
@@ -84,12 +85,41 @@ var (
 		{lookFor: "xml\\.", packageName: "encoding/xml"},
 		{lookFor: "yaml\\.", packageName: "gopkg.in/yaml.v2"},
 	}
+
+	importMapping = map[string]goImport{}
 )
 
+func constructImportMapping(input map[string]string) map[string]goImport {
+	var (
+		nameToAlias = map[string]string{}
+		result      = map[string]goImport{}
+	)
+
+	{
+		var packagePaths []string
+		for _, packageName := range input {
+			packagePaths = append(packagePaths, packageName)
+		}
+		sort.Strings(packagePaths)
+
+		for _, packageName := range packagePaths {
+			if _, ok := nameToAlias[packageName]; !ok {
+				nameToAlias[packageName] = fmt.Sprintf("externalRef%d", len(nameToAlias))
+			}
+		}
+	}
+	for urlOrPath, packageName := range input {
+		result[urlOrPath] = goImport{alias: nameToAlias[packageName], packageName: packageName}
+	}
+	return result
+}
+
 // Uses the Go templating engine to generate all of our server wrappers from
 // the descriptions we've built up above from the schema objects.
 // opts defines
 func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (string, error) {
+	importMapping = constructImportMapping(opts.ImportMapping)
+
 	filterOperationsByTag(swagger, opts)
 	if !opts.SkipPrune {
 		pruneUnusedComponents(swagger)
@@ -169,23 +199,40 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
 
 	// Imports needed for the generated code to compile
 	var imports []string
+	for _, importGo := range importMapping {
+		imports = append(imports, importGo.String())
+	}
 
 	var buf bytes.Buffer
 	w := bufio.NewWriter(&buf)
 
 	// Based on module prefixes, figure out which optional imports are required.
+	pkgs := make(map[string]int)
 	for _, str := range []string{typeDefinitions, chiServerOut, echoServerOut, clientOut, clientWithResponsesOut, inlinedSpec} {
-		for _, goImport := range allGoImports {
-			match, err := regexp.MatchString(fmt.Sprintf("[^a-zA-Z0-9_]%s", goImport.lookFor), str)
-			if err != nil {
-				return "", errors.Wrap(err, "error figuring out imports")
+		for _, line := range strings.Split(strings.TrimSpace(str), "\n") {
+			line = strings.TrimSpace(line)
+
+			if line == "" || strings.HasPrefix(line, "//") {
+				continue
 			}
-			if match {
-				imports = append(imports, goImport.String())
+
+			for _, goImport := range allGoImports {
+				match, err := regexp.MatchString(fmt.Sprintf("[^a-zA-Z0-9_]%s", goImport.lookFor), line)
+				if err != nil {
+					return "", errors.Wrap(err, "error figuring out imports")
+				}
+
+				if match {
+					pkgs[goImport.String()]++
+				}
 			}
 		}
 	}
 
+	for k := range pkgs {
+		imports = append(imports, k)
+	}
+
 	importsOut, err := GenerateImports(t, imports, packageName)
 	if err != nil {
 		return "", errors.Wrap(err, "error generating imports")
diff --git a/pkg/codegen/prune.go b/pkg/codegen/prune.go
index 41783ad..a2804f0 100644
--- a/pkg/codegen/prune.go
+++ b/pkg/codegen/prune.go
@@ -27,6 +27,9 @@ func walkSwagger(swagger *openapi3.Swagger, doFn func(RefWrapper) (bool, error))
 	}
 
 	for _, p := range swagger.Paths {
+		for _, param := range p.Parameters {
+			walkParameterRef(param, doFn)
+		}
 		for _, op := range p.Operations() {
 			walkOperation(op, doFn)
 		}
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index c3983d6..c9ad2ea 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -13,7 +13,7 @@ type Schema struct {
 	GoType  string // The Go type needed to represent the schema
 	RefType string // If the type has a type name, this is set
 
-	EnumValues []string // Enum values
+	EnumValues map[string]string // Enum values
 
 	Properties               []Property       // For an object, the fields with names
 	HasAdditionalProperties  bool             // Whether we support additional properties
@@ -253,13 +253,19 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 			}
 			outSchema.GoType = "bool"
 		case "string":
-			for _, enumValue := range schema.Enum {
-				outSchema.EnumValues = append(outSchema.EnumValues, enumValue.(string))
+			enumValues := make([]string, len(schema.Enum))
+			for i, enumValue := range schema.Enum {
+				enumValues[i] = enumValue.(string)
 			}
+
+			outSchema.EnumValues = SanitizeEnumNames(enumValues)
+
 			// Special case string formats here.
 			switch f {
 			case "byte":
 				outSchema.GoType = "[]byte"
+			case "email":
+				outSchema.GoType = "openapi_types.Email"
 			case "date":
 				outSchema.GoType = "openapi_types.Date"
 			case "date-time":
@@ -421,6 +427,17 @@ func GenStructFromAllOf(allOf []*openapi3.SchemaRef, path []string) (string, err
 			objectParts = append(objectParts, "   // Embedded fields due to inline allOf schema")
 			objectParts = append(objectParts, GenFieldsFromProperties(goSchema.Properties)...)
 
+			if goSchema.HasAdditionalProperties {
+				addPropsType := goSchema.AdditionalPropertiesType.GoType
+				if goSchema.AdditionalPropertiesType.RefType != "" {
+					addPropsType = goSchema.AdditionalPropertiesType.RefType
+				}
+
+				additionalPropertiesPart := fmt.Sprintf("AdditionalProperties map[string]%s `json:\"-\"`", addPropsType)
+				if !StringInArray(additionalPropertiesPart, objectParts) {
+					objectParts = append(objectParts, additionalPropertiesPart)
+				}
+			}
 		}
 	}
 	objectParts = append(objectParts, "}")
diff --git a/pkg/codegen/templates/chi-handler.tmpl b/pkg/codegen/templates/chi-handler.tmpl
index 93cce45..16cd461 100644
--- a/pkg/codegen/templates/chi-handler.tmpl
+++ b/pkg/codegen/templates/chi-handler.tmpl
@@ -5,9 +5,12 @@ func Handler(si ServerInterface) http.Handler {
 
 // HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
 func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
+{{if .}}wrapper := ServerInterfaceWrapper{
+        Handler: si,
+    }
+{{end}}
 {{range .}}r.Group(func(r chi.Router) {
-  r.Use({{.OperationId}}Ctx)
-  r.{{.Method | lower | title }}("{{.Path | swaggerUriToChiUri}}", si.{{.OperationId}})
+  r.{{.Method | lower | title }}("{{.Path | swaggerUriToChiUri}}", wrapper.{{.OperationId}})
 })
 {{end}}
   return r
diff --git a/pkg/codegen/templates/chi-interface.tmpl b/pkg/codegen/templates/chi-interface.tmpl
index fd23067..79a51fd 100644
--- a/pkg/codegen/templates/chi-interface.tmpl
+++ b/pkg/codegen/templates/chi-interface.tmpl
@@ -1,5 +1,7 @@
+// ServerInterface represents all server handlers.
 type ServerInterface interface {
-{{range .}}// {{.Summary | stripNewLines }} ({{.Method}} {{.Path}})
-{{.OperationId}}(w http.ResponseWriter, r *http.Request)
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}})
 {{end}}
 }
diff --git a/pkg/codegen/templates/chi-middleware.tmpl b/pkg/codegen/templates/chi-middleware.tmpl
index 4ec3b39..ac9ee23 100644
--- a/pkg/codegen/templates/chi-middleware.tmpl
+++ b/pkg/codegen/templates/chi-middleware.tmpl
@@ -1,173 +1,165 @@
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+    Handler ServerInterface
+}
 
 {{range .}}{{$opid := .OperationId}}
 
-{{if .RequiresParamObject}}
-// ParamsFor{{.OperationId}} operation parameters from context
-func ParamsFor{{.OperationId}}(ctx context.Context) *{{.OperationId}}Params {
-  return ctx.Value("{{.OperationId}}Params").(*{{.OperationId}}Params)
-}
-{{end}}
-
 // {{$opid}} operation middleware
-func {{$opid}}Ctx(next http.Handler) http.Handler {
-  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-    ctx := r.Context()
-    {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
-    var err error
-    {{end}}
-
-    {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
-    var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
-
-    {{if .IsPassThrough}}
-    {{$varName}} = chi.URLParam(r, "{{.ParamName}}")
-    {{end}}
-    {{if .IsJson}}
-    err = json.Unmarshal([]byte(chi.URLParam(r, "{{.ParamName}}")), &{{$varName}})
-    if err != nil {
-      http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-      return
-    }
-    {{end}}
-    {{if .IsStyled}}
-    err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
-    if err != nil {
-      http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
-      return
-    }
-    {{end}}
-
-    ctx = context.WithValue(ctx, "{{$varName}}", {{$varName}})
-    {{end}}
+func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) {
+  ctx := r.Context()
+  {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
+  var err error
+  {{end}}
+
+  {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
+  var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+
+  {{if .IsPassThrough}}
+  {{$varName}} = chi.URLParam(r, "{{.ParamName}}")
+  {{end}}
+  {{if .IsJson}}
+  err = json.Unmarshal([]byte(chi.URLParam(r, "{{.ParamName}}")), &{{$varName}})
+  if err != nil {
+    http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
+    return
+  }
+  {{end}}
+  {{if .IsStyled}}
+  err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
+  if err != nil {
+    http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+    return
+  }
+  {{end}}
+
+  {{end}}
 
 {{range .SecurityDefinitions}}
-    ctx = context.WithValue(ctx, "{{.ProviderName}}.Scopes", {{toStringArray .Scopes}})
+  ctx = context.WithValue(ctx, "{{.ProviderName}}.Scopes", {{toStringArray .Scopes}})
 {{end}}
 
-    {{if .RequiresParamObject}}
-      // Parameter object where we will unmarshal all parameters from the context
-      var params {{.OperationId}}Params
+  {{if .RequiresParamObject}}
+    // Parameter object where we will unmarshal all parameters from the context
+    var params {{.OperationId}}Params
 
-      {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
-        if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
+    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
 
-        {{if .IsPassThrough}}
-          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
-        {{end}}
-
-        {{if .IsJson}}
-          var value {{.TypeDef}}
-          err = json.Unmarshal([]byte(paramValue), &value)
-          if err != nil {
-            http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-            return
-          }
+      {{if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+      {{end}}
 
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
-        {{end}}
-        }{{if .Required}} else {
-            http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
-            return
-        }{{end}}
-        {{if .IsStyled}}
-        err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
+      {{if .IsJson}}
+        var value {{.TypeDef}}
+        err = json.Unmarshal([]byte(paramValue), &value)
         if err != nil {
-          http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+          http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
           return
         }
-        {{end}}
-    {{end}}
-
-      {{if .HeaderParams}}
-        headers := r.Header
-
-        {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
-          if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
-            var {{.GoName}} {{.TypeDef}}
-            n := len(valueList)
-            if n != 1 {
-              http.Error(w, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n), http.StatusBadRequest)
-              return
-            }
-
-          {{if .IsPassThrough}}
-            params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
-          {{end}}
-
-          {{if .IsJson}}
-            err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
-            if err != nil {
-              http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-              return
-            }
-          {{end}}
-
-          {{if .IsStyled}}
-            err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", valueList[0], &{{.GoName}})
-            if err != nil {
-              http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
-              return
-            }
-          {{end}}
-
-            params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
-
-          } {{if .Required}}else {
-              http.Error(w, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
-              return
-          }{{end}}
 
-        {{end}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
       {{end}}
+      }{{if .Required}} else {
+          http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+          return
+      }{{end}}
+      {{if .IsStyled}}
+      err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
+      if err != nil {
+        http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+        return
+      }
+      {{end}}
+  {{end}}
 
-      {{range .CookieParams}}
-        if cookie, err := r.Cookie("{{.ParamName}}"); err == nil {
-
-        {{- if .IsPassThrough}}
-          params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
-        {{end}}
+    {{if .HeaderParams}}
+      headers := r.Header
 
-        {{- if .IsJson}}
-          var value {{.TypeDef}}
-          var decoded string
-          decoded, err := url.QueryUnescape(cookie.Value)
-          if err != nil {
-            http.Error(w, "Error unescaping cookie parameter '{{.ParamName}}'", http.StatusBadRequest)
+      {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
+        if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
+          var {{.GoName}} {{.TypeDef}}
+          n := len(valueList)
+          if n != 1 {
+            http.Error(w, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n), http.StatusBadRequest)
             return
           }
 
-          err = json.Unmarshal([]byte(decoded), &value)
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
+        {{end}}
+
+        {{if .IsJson}}
+          err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
           if err != nil {
             http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
             return
           }
-
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
         {{end}}
 
-        {{- if .IsStyled}}
-          var value {{.TypeDef}}
-          err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value)
+        {{if .IsStyled}}
+          err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", valueList[0], &{{.GoName}})
           if err != nil {
-            http.Error(w, "Invalid format for parameter {{.ParamName}}: %s", http.StatusBadRequest)
+            http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
             return
           }
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
         {{end}}
 
+          params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
+
+        } {{if .Required}}else {
+            http.Error(w, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
+            return
+        }{{end}}
+
+      {{end}}
+    {{end}}
+
+    {{range .CookieParams}}
+      if cookie, err := r.Cookie("{{.ParamName}}"); err == nil {
+
+      {{- if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
+      {{end}}
+
+      {{- if .IsJson}}
+        var value {{.TypeDef}}
+        var decoded string
+        decoded, err := url.QueryUnescape(cookie.Value)
+        if err != nil {
+          http.Error(w, "Error unescaping cookie parameter '{{.ParamName}}'", http.StatusBadRequest)
+          return
         }
 
-        {{- if .Required}} else {
-          http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+        err = json.Unmarshal([]byte(decoded), &value)
+        if err != nil {
+          http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
           return
         }
-        {{- end}}
+
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
       {{end}}
 
-      ctx = context.WithValue(ctx, "{{.OperationId}}Params", &params)
+      {{- if .IsStyled}}
+        var value {{.TypeDef}}
+        err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value)
+        if err != nil {
+          http.Error(w, "Invalid format for parameter {{.ParamName}}: %s", http.StatusBadRequest)
+          return
+        }
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      }
+
+      {{- if .Required}} else {
+        http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+        return
+      }
+      {{- end}}
     {{end}}
-    next.ServeHTTP(w, r.WithContext(ctx))
-  })
+  {{end}}
+  siw.Handler.{{.OperationId}}(w, r.WithContext(ctx){{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
 }
 {{end}}
 
diff --git a/pkg/codegen/templates/templates.gen.go b/pkg/codegen/templates/templates.gen.go
index 491eda3..9b90173 100644
--- a/pkg/codegen/templates/templates.gen.go
+++ b/pkg/codegen/templates/templates.gen.go
@@ -80,190 +80,187 @@ func Handler(si ServerInterface) http.Handler {
 
 // HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
 func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
+{{if .}}wrapper := ServerInterfaceWrapper{
+        Handler: si,
+    }
+{{end}}
 {{range .}}r.Group(func(r chi.Router) {
-  r.Use({{.OperationId}}Ctx)
-  r.{{.Method | lower | title }}("{{.Path | swaggerUriToChiUri}}", si.{{.OperationId}})
+  r.{{.Method | lower | title }}("{{.Path | swaggerUriToChiUri}}", wrapper.{{.OperationId}})
 })
 {{end}}
   return r
 }
 `,
-	"chi-interface.tmpl": `type ServerInterface interface {
-{{range .}}// {{.Summary | stripNewLines }} ({{.Method}} {{.Path}})
-{{.OperationId}}(w http.ResponseWriter, r *http.Request)
+	"chi-interface.tmpl": `// ServerInterface represents all server handlers.
+type ServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{.OperationId}}(w http.ResponseWriter, r *http.Request{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}})
 {{end}}
 }
 `,
-	"chi-middleware.tmpl": `
-{{range .}}{{$opid := .OperationId}}
-
-{{if .RequiresParamObject}}
-// ParamsFor{{.OperationId}} operation parameters from context
-func ParamsFor{{.OperationId}}(ctx context.Context) *{{.OperationId}}Params {
-  return ctx.Value("{{.OperationId}}Params").(*{{.OperationId}}Params)
+	"chi-middleware.tmpl": `// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+    Handler ServerInterface
 }
-{{end}}
 
-// {{$opid}} operation middleware
-func {{$opid}}Ctx(next http.Handler) http.Handler {
-  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-    ctx := r.Context()
-    {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
-    var err error
-    {{end}}
-
-    {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
-    var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+{{range .}}{{$opid := .OperationId}}
 
-    {{if .IsPassThrough}}
-    {{$varName}} = chi.URLParam(r, "{{.ParamName}}")
-    {{end}}
-    {{if .IsJson}}
-    err = json.Unmarshal([]byte(chi.URLParam(r, "{{.ParamName}}")), &{{$varName}})
-    if err != nil {
-      http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-      return
-    }
-    {{end}}
-    {{if .IsStyled}}
-    err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
-    if err != nil {
-      http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
-      return
-    }
-    {{end}}
+// {{$opid}} operation middleware
+func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Request) {
+  ctx := r.Context()
+  {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
+  var err error
+  {{end}}
+
+  {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
+  var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+
+  {{if .IsPassThrough}}
+  {{$varName}} = chi.URLParam(r, "{{.ParamName}}")
+  {{end}}
+  {{if .IsJson}}
+  err = json.Unmarshal([]byte(chi.URLParam(r, "{{.ParamName}}")), &{{$varName}})
+  if err != nil {
+    http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
+    return
+  }
+  {{end}}
+  {{if .IsStyled}}
+  err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}})
+  if err != nil {
+    http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+    return
+  }
+  {{end}}
 
-    ctx = context.WithValue(ctx, "{{$varName}}", {{$varName}})
-    {{end}}
+  {{end}}
 
 {{range .SecurityDefinitions}}
-    ctx = context.WithValue(ctx, "{{.ProviderName}}.Scopes", {{toStringArray .Scopes}})
+  ctx = context.WithValue(ctx, "{{.ProviderName}}.Scopes", {{toStringArray .Scopes}})
 {{end}}
 
-    {{if .RequiresParamObject}}
-      // Parameter object where we will unmarshal all parameters from the context
-      var params {{.OperationId}}Params
-
-      {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
-        if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
+  {{if .RequiresParamObject}}
+    // Parameter object where we will unmarshal all parameters from the context
+    var params {{.OperationId}}Params
 
-        {{if .IsPassThrough}}
-          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
-        {{end}}
+    {{range $paramIdx, $param := .QueryParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      if paramValue := r.URL.Query().Get("{{.ParamName}}"); paramValue != "" {
 
-        {{if .IsJson}}
-          var value {{.TypeDef}}
-          err = json.Unmarshal([]byte(paramValue), &value)
-          if err != nil {
-            http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-            return
-          }
+      {{if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+      {{end}}
 
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
-        {{end}}
-        }{{if .Required}} else {
-            http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
-            return
-        }{{end}}
-        {{if .IsStyled}}
-        err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
+      {{if .IsJson}}
+        var value {{.TypeDef}}
+        err = json.Unmarshal([]byte(paramValue), &value)
         if err != nil {
-          http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+          http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
           return
         }
-        {{end}}
-    {{end}}
-
-      {{if .HeaderParams}}
-        headers := r.Header
-
-        {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
-          if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
-            var {{.GoName}} {{.TypeDef}}
-            n := len(valueList)
-            if n != 1 {
-              http.Error(w, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n), http.StatusBadRequest)
-              return
-            }
-
-          {{if .IsPassThrough}}
-            params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
-          {{end}}
-
-          {{if .IsJson}}
-            err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
-            if err != nil {
-              http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
-              return
-            }
-          {{end}}
-
-          {{if .IsStyled}}
-            err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", valueList[0], &{{.GoName}})
-            if err != nil {
-              http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
-              return
-            }
-          {{end}}
-
-            params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
 
-          } {{if .Required}}else {
-              http.Error(w, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
-              return
-          }{{end}}
-
-        {{end}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
       {{end}}
+      }{{if .Required}} else {
+          http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+          return
+      }{{end}}
+      {{if .IsStyled}}
+      err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", r.URL.Query(), &params.{{.GoName}})
+      if err != nil {
+        http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
+        return
+      }
+      {{end}}
+  {{end}}
 
-      {{range .CookieParams}}
-        if cookie, err := r.Cookie("{{.ParamName}}"); err == nil {
-
-        {{- if .IsPassThrough}}
-          params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
-        {{end}}
+    {{if .HeaderParams}}
+      headers := r.Header
 
-        {{- if .IsJson}}
-          var value {{.TypeDef}}
-          var decoded string
-          decoded, err := url.QueryUnescape(cookie.Value)
-          if err != nil {
-            http.Error(w, "Error unescaping cookie parameter '{{.ParamName}}'", http.StatusBadRequest)
+      {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
+        if valueList, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
+          var {{.GoName}} {{.TypeDef}}
+          n := len(valueList)
+          if n != 1 {
+            http.Error(w, fmt.Sprintf("Expected one value for {{.ParamName}}, got %d", n), http.StatusBadRequest)
             return
           }
 
-          err = json.Unmarshal([]byte(decoded), &value)
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}valueList[0]
+        {{end}}
+
+        {{if .IsJson}}
+          err = json.Unmarshal([]byte(valueList[0]), &{{.GoName}})
           if err != nil {
             http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
             return
           }
-
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
         {{end}}
 
-        {{- if .IsStyled}}
-          var value {{.TypeDef}}
-          err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value)
+        {{if .IsStyled}}
+          err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", valueList[0], &{{.GoName}})
           if err != nil {
-            http.Error(w, "Invalid format for parameter {{.ParamName}}: %s", http.StatusBadRequest)
+            http.Error(w, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err), http.StatusBadRequest)
             return
           }
-          params.{{.GoName}} = {{if not .Required}}&{{end}}value
         {{end}}
 
+          params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
+
+        } {{if .Required}}else {
+            http.Error(w, fmt.Sprintf("Header parameter {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
+            return
+        }{{end}}
+
+      {{end}}
+    {{end}}
+
+    {{range .CookieParams}}
+      if cookie, err := r.Cookie("{{.ParamName}}"); err == nil {
+
+      {{- if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}cookie.Value
+      {{end}}
+
+      {{- if .IsJson}}
+        var value {{.TypeDef}}
+        var decoded string
+        decoded, err := url.QueryUnescape(cookie.Value)
+        if err != nil {
+          http.Error(w, "Error unescaping cookie parameter '{{.ParamName}}'", http.StatusBadRequest)
+          return
         }
 
-        {{- if .Required}} else {
-          http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+        err = json.Unmarshal([]byte(decoded), &value)
+        if err != nil {
+          http.Error(w, "Error unmarshaling parameter '{{.ParamName}}' as JSON", http.StatusBadRequest)
+          return
+        }
+
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      {{- if .IsStyled}}
+        var value {{.TypeDef}}
+        err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value)
+        if err != nil {
+          http.Error(w, "Invalid format for parameter {{.ParamName}}: %s", http.StatusBadRequest)
           return
         }
-        {{- end}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
       {{end}}
 
-      ctx = context.WithValue(ctx, "{{.OperationId}}Params", &params)
+      }
+
+      {{- if .Required}} else {
+        http.Error(w, "Query argument {{.ParamName}} is required, but not found", http.StatusBadRequest)
+        return
+      }
+      {{- end}}
     {{end}}
-    next.ServeHTTP(w, r.WithContext(ctx))
-  })
+  {{end}}
+  siw.Handler.{{.OperationId}}(w, r.WithContext(ctx){{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
 }
 {{end}}
 
@@ -758,8 +755,8 @@ type {{.TypeName}} {{.Schema.TypeDecl}}
 // List of {{ .TypeName }}
 const (
 	{{- $typeName := .TypeName }}
-    {{- range .Schema.EnumValues }}
-    {{ $typeName }}_{{ . }} {{ $typeName }} = "{{ . }}"
+    {{- range $key, $value := .Schema.EnumValues }}
+    {{ $typeName }}_{{ $key }} {{ $typeName }} = "{{ $value }}"
     {{- end }}
 )
 {{- end }}
diff --git a/pkg/codegen/templates/typedef.tmpl b/pkg/codegen/templates/typedef.tmpl
index 4f20b07..170984f 100644
--- a/pkg/codegen/templates/typedef.tmpl
+++ b/pkg/codegen/templates/typedef.tmpl
@@ -5,8 +5,8 @@ type {{.TypeName}} {{.Schema.TypeDecl}}
 // List of {{ .TypeName }}
 const (
 	{{- $typeName := .TypeName }}
-    {{- range .Schema.EnumValues }}
-    {{ $typeName }}_{{ . }} {{ $typeName }} = "{{ . }}"
+    {{- range $key, $value := .Schema.EnumValues }}
+    {{ $typeName }}_{{ $key }} {{ $typeName }} = "{{ $value }}"
     {{- end }}
 )
 {{- end }}
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index 2a35b03..6c577d9 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -17,6 +17,7 @@ import (
 	"fmt"
 	"regexp"
 	"sort"
+	"strconv"
 	"strings"
 	"unicode"
 
@@ -65,7 +66,7 @@ func ToCamelCase(str string) string {
 		if unicode.IsUpper(v) {
 			n += string(v)
 		}
-			if unicode.IsDigit(v) {
+		if unicode.IsDigit(v) {
 			n += string(v)
 		}
 		if unicode.IsLower(v) {
@@ -76,7 +77,7 @@ func ToCamelCase(str string) string {
 			}
 		}
 
-		 if strings.ContainsRune(separators, v) {
+		if strings.ContainsRune(separators, v) {
 			capNext = true
 		} else {
 			capNext = false
@@ -197,20 +198,31 @@ func StringInArray(str string, array []string) bool {
 // #/components/schemas/Foo -> Foo
 // #/components/parameters/Bar -> Bar
 // #/components/responses/Baz -> Baz
-// Remote components (document.json#/Foo) are not yet supported
-// URL components (http://deepmap.com/schemas/document.json#Foo) are not yet
-// supported
-// We only support flat components for now, so no components in a schema under
-// components.
+// Remote components (document.json#/Foo) are supported if they present in --import-mapping
+// URL components (http://deepmap.com/schemas/document.json#Foo) are supported if they present in --import-mapping
+//
 func RefPathToGoType(refPath string) (string, error) {
-	pathParts := strings.Split(refPath, "/")
-	if pathParts[0] != "#" {
-		return "", errors.New("Only local document components are supported")
+	if refPath[0] == '#' {
+		pathParts := strings.Split(refPath, "/")
+		if len(pathParts) != 4 {
+			return "", errors.New("Parameter nesting is deeper than supported")
+		}
+		return SchemaNameToTypeName(pathParts[3]), nil
 	}
-	if len(pathParts) != 4 {
-		return "", errors.New("Parameter nesting is deeper than supported")
+	pathParts := strings.Split(refPath, "#")
+	if len(pathParts) != 2 {
+		return "", fmt.Errorf("unsupported reference: %s", refPath)
+	}
+	remoteComponent, flatComponent := pathParts[0], pathParts[1]
+	if goImport, ok := importMapping[remoteComponent]; !ok {
+		return "", fmt.Errorf("unrecognized external reference '%s'; please provide the known import for this reference using option --import-mapping", remoteComponent)
+	} else {
+		goType, err := RefPathToGoType("#" + flatComponent)
+		if err != nil {
+			return "", err
+		}
+		return fmt.Sprintf("%s.%s", goImport.alias, goType), nil
 	}
-	return SchemaNameToTypeName(pathParts[3]), nil
 }
 
 // This function converts a swagger style path URI with parameters to a
@@ -317,6 +329,155 @@ func IsGoKeyword(str string) bool {
 	return false
 }
 
+// IsPredeclaredGoIdentifier returns whether the given string
+// is a predefined go indentifier.
+//
+// See https://golang.org/ref/spec#Predeclared_identifiers
+func IsPredeclaredGoIdentifier(str string) bool {
+	predeclaredIdentifiers := []string{
+		// Types
+		"bool",
+		"byte",
+		"complex64",
+		"complex128",
+		"error",
+		"float32",
+		"float64",
+		"int",
+		"int8",
+		"int16",
+		"int32",
+		"int64",
+		"rune",
+		"string",
+		"uint",
+		"uint8",
+		"uint16",
+		"uint32",
+		"uint64",
+		"uintptr",
+		// Constants
+		"true",
+		"false",
+		"iota",
+		// Zero value
+		"nil",
+		// Functions
+		"append",
+		"cap",
+		"close",
+		"complex",
+		"copy",
+		"delete",
+		"imag",
+		"len",
+		"make",
+		"new",
+		"panic",
+		"print",
+		"println",
+		"real",
+		"recover",
+	}
+
+	for _, k := range predeclaredIdentifiers {
+		if k == str {
+			return true
+		}
+	}
+
+	return false
+}
+
+// IsGoIdentity checks if the given string can be used as an identity
+// in the generated code like a type name or constant name.
+//
+// See https://golang.org/ref/spec#Identifiers
+func IsGoIdentity(str string) bool {
+	for i, c := range str {
+		if !isValidRuneForGoID(i, c) {
+			return false
+		}
+	}
+
+	return IsGoKeyword(str)
+}
+
+func isValidRuneForGoID(index int, char rune) bool {
+	if index == 0 && unicode.IsNumber(char) {
+		return false
+	}
+
+	return unicode.IsLetter(char) || char == '_' || unicode.IsNumber(char)
+}
+
+// IsValidGoIdentity checks if the given string can be used as a
+// name of variable, constant, or type.
+func IsValidGoIdentity(str string) bool {
+	if IsGoIdentity(str) {
+		return false
+	}
+
+	return !IsPredeclaredGoIdentifier(str)
+}
+
+// SanitizeGoIdentity deletes and replaces the illegal runes in the given
+// string to use the string as a valid identity.
+func SanitizeGoIdentity(str string) string {
+	sanitized := []rune(str)
+
+	for i, c := range sanitized {
+		if !isValidRuneForGoID(i, c) {
+			sanitized[i] = '_'
+		} else {
+			sanitized[i] = c
+		}
+	}
+
+	str = string(sanitized)
+
+	if IsGoKeyword(str) || IsPredeclaredGoIdentifier(str) {
+		str = "_" + str
+	}
+
+	if !IsValidGoIdentity(str) {
+		panic("here is a bug")
+	}
+
+	return str
+}
+
+// SanitizeEnumNames fixes illegal chars in the enum names
+// and removes duplicates
+func SanitizeEnumNames(enumNames []string) map[string]string {
+	dupCheck := make(map[string]int, len(enumNames))
+	deDup := make([]string, 0, len(enumNames))
+
+	for _, n := range enumNames {
+		if _, dup := dupCheck[n]; !dup {
+			deDup = append(deDup, n)
+		}
+
+		dupCheck[n] = 0
+	}
+
+	dupCheck = make(map[string]int, len(deDup))
+	sanitizedDeDup := make(map[string]string, len(deDup))
+
+	for _, n := range deDup {
+		sanitized := SanitizeGoIdentity(n)
+
+		if _, dup := dupCheck[sanitized]; !dup {
+			sanitizedDeDup[sanitized] = n
+			dupCheck[sanitized]++
+		} else {
+			sanitizedDeDup[sanitized+strconv.Itoa(dupCheck[sanitized])] = n
+		}
+	}
+
+	return sanitizedDeDup
+}
+
 // Converts a Schema name to a valid Go type name. It converts to camel case, and makes sure the name is
 // valid in Go
 func SchemaNameToTypeName(name string) string {
diff --git a/pkg/types/email.go b/pkg/types/email.go
new file mode 100644
index 0000000..00a4cf6
--- /dev/null
+++ b/pkg/types/email.go
@@ -0,0 +1,27 @@
+package types
+
+import (
+	"encoding/json"
+	"errors"
+)
+
+type Email string
+
+func (e Email) MarshalJSON() ([]byte, error) {
+	if !emailRegex.MatchString(string(e)) {
+		return nil, errors.New("email: failed to pass regex validation")
+	}
+	return json.Marshal(string(e))
+}
+
+func (e *Email) UnmarshalJSON(data []byte) error {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return err
+	}
+	if !emailRegex.MatchString(s) {
+		return errors.New("email: failed to pass regex validation")
+	}
+	*e = Email(s)
+	return nil
+}
diff --git a/pkg/types/regexes.go b/pkg/types/regexes.go
new file mode 100644
index 0000000..94f17df
--- /dev/null
+++ b/pkg/types/regexes.go
@@ -0,0 +1,11 @@
+package types
+
+import "regexp"
+
+const (
+	emailRegexString = "^(?:(?:(?:(?:[a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(?:\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|(?:(?:\\x22)(?:(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(?:\\x20|\\x09)+)?(?:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(?:(?:(?:\\x20|\\x09)*(?:\\x0d\\x0a))?(\\x20|\\x09)+)?(?:\\x22))))@(?:(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(?:(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])(?:[a-zA-Z]|\\d|-|\\.|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*(?:[a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$"
+)
+
+var (
+	emailRegex = regexp.MustCompile(emailRegexString)
+)
diff --git a/pkg/util/inputmapping.go b/pkg/util/inputmapping.go
new file mode 100644
index 0000000..3557ce5
--- /dev/null
+++ b/pkg/util/inputmapping.go
@@ -0,0 +1,60 @@
+package util
+
+import (
+	"fmt"
+	"strings"
+)
+
+// The input mapping is experessed on the command line as `key1:value1,key2:value2,...`
+// We parse it here, but need to keep in mind that keys or values may contain
+// commas and colons. We will allow escaping those using double quotes, so
+// when passing in "key1":"value1", we will not look inside the quoted sections.
+func ParseCommandlineMap(src string) (map[string]string, error) {
+	result := make(map[string]string)
+	tuples := splitString(src, ',')
+	for _, t := range tuples {
+		kv := splitString(t, ':')
+		if len(kv) != 2 {
+			return nil, fmt.Errorf("expected key:value, got :%s", t)
+		}
+		key := strings.TrimLeft(kv[0], `"`)
+		key = strings.TrimRight(key, `"`)
+
+		value := strings.TrimLeft(kv[1], `"`)
+		value = strings.TrimRight(value, `"`)
+
+		result[key] = value
+	}
+	return result, nil
+}
+
+// This function splits a string along the specifed separator, but it
+// ignores anything between double quotes for splitting. We do simple
+// inside/outside quote counting. Quotes are not stripped from output.
+func splitString(s string, sep rune) ([]string) {
+	const escapeChar rune = '"'
+
+	var parts []string
+	var part string
+	inQuotes := false
+
+	for _, c := range s {
+		if c == escapeChar{
+			if inQuotes {
+				inQuotes = false
+			} else {
+				inQuotes = true
+			}
+		}
+
+		// If we've gotten the separator rune, consider the previous part
+		// complete, but only if we're outside of quoted sections
+		if c == sep && !inQuotes {
+			parts = append(parts, part)
+			part = ""
+			continue
+		}
+		part = part + string(c)
+	}
+	return append(parts, part)
+}
diff --git a/pkg/util/loader.go b/pkg/util/loader.go
index 6538e24..56b0297 100644
--- a/pkg/util/loader.go
+++ b/pkg/util/loader.go
@@ -1,32 +1,13 @@
 package util
 
 import (
-	"fmt"
-	"io/ioutil"
-	"path/filepath"
-	"strings"
-
 	"github.com/getkin/kin-openapi/openapi3"
 )
 
-func LoadSwagger(filePath string) (*openapi3.Swagger, error) {
-	data, err := ioutil.ReadFile(filePath)
-	if err != nil {
-		return nil, err
-	}
-
-	var swagger *openapi3.Swagger
-	ext := filepath.Ext(filePath)
-	ext = strings.ToLower(ext)
-	switch ext {
-	// The YAML handler can parse both YAML and JSON
-	case ".yaml", ".yml", ".json":
-		swagger, err = openapi3.NewSwaggerLoader().LoadSwaggerFromData(data)
-	default:
-		return nil, fmt.Errorf("%s is not a supported extension, use .yaml, .yml or .json", ext)
-	}
-	if err != nil {
-		return nil, err
-	}
-	return swagger, nil
+func LoadSwagger(filePath string) (swagger *openapi3.Swagger, err error) {
+	loader := openapi3.NewSwaggerLoader()
+	loader.IsExternalRefsAllowed = true
+	swagger, err = loader.LoadSwaggerFromFile(filePath)
+	return
 }
+


Test patch:
diff --git a/internal/test/externalref/doc.go b/internal/test/externalref/doc.go
new file mode 100644
index 0000000..e239ead
--- /dev/null
+++ b/internal/test/externalref/doc.go
@@ -0,0 +1,3 @@
+package externalref
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=externalref -o externalref.gen.go --import-mapping=./packageA/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageA,./packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml
diff --git a/internal/test/externalref/externalref.gen.go b/internal/test/externalref/externalref.gen.go
new file mode 100644
index 0000000..5fc09bd
--- /dev/null
+++ b/internal/test/externalref/externalref.gen.go
@@ -0,0 +1,15 @@
+// Package externalref provides primitives to interact the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
+package externalref
+
+import (
+	externalRef0 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageA"
+	externalRef1 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageB"
+)
+
+// Container defines model for Container.
+type Container struct {
+	ObjectA *externalRef0.ObjectA `json:"object_a,omitempty"`
+	ObjectB *externalRef1.ObjectB `json:"object_b,omitempty"`
+}
diff --git a/internal/test/externalref/imports_test.go b/internal/test/externalref/imports_test.go
new file mode 100644
index 0000000..284964a
--- /dev/null
+++ b/internal/test/externalref/imports_test.go
@@ -0,0 +1,16 @@
+package externalref
+
+import (
+	"testing"
+
+	"github.com/deepmap/oapi-codegen/internal/test/externalref/packageA"
+	"github.com/deepmap/oapi-codegen/internal/test/externalref/packageB"
+)
+
+func TestParameters(t *testing.T) {
+	b := &packageB.ObjectB{}
+	_ = Container{
+		ObjectA: &packageA.ObjectA{ObjectB: b},
+		ObjectB: b,
+	}
+}
diff --git a/internal/test/externalref/packageA/doc.go b/internal/test/externalref/packageA/doc.go
new file mode 100644
index 0000000..410765f
--- /dev/null
+++ b/internal/test/externalref/packageA/doc.go
@@ -0,0 +1,3 @@
+package packageA
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=packageA -o externalref.gen.go --import-mapping=../packageB/spec.yaml:github.com/deepmap/oapi-codegen/internal/test/externalref/packageB spec.yaml
diff --git a/internal/test/externalref/packageA/externalref.gen.go b/internal/test/externalref/packageA/externalref.gen.go
new file mode 100644
index 0000000..066f2b7
--- /dev/null
+++ b/internal/test/externalref/packageA/externalref.gen.go
@@ -0,0 +1,14 @@
+// Package packageA provides primitives to interact the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
+package packageA
+
+import (
+	externalRef0 "github.com/deepmap/oapi-codegen/internal/test/externalref/packageB"
+)
+
+// ObjectA defines model for ObjectA.
+type ObjectA struct {
+	Name    *string               `json:"name,omitempty"`
+	ObjectB *externalRef0.ObjectB `json:"object_b,omitempty"`
+}
diff --git a/internal/test/externalref/packageA/spec.yaml b/internal/test/externalref/packageA/spec.yaml
new file mode 100644
index 0000000..b2386a0
--- /dev/null
+++ b/internal/test/externalref/packageA/spec.yaml
@@ -0,0 +1,8 @@
+components:
+  schemas:
+    ObjectA:
+      properties:
+        name:
+          type: string
+        object_b:
+          $ref: ../packageB/spec.yaml#/components/schemas/ObjectB
\ No newline at end of file
diff --git a/internal/test/externalref/packageB/doc.go b/internal/test/externalref/packageB/doc.go
new file mode 100644
index 0000000..4d28c8c
--- /dev/null
+++ b/internal/test/externalref/packageB/doc.go
@@ -0,0 +1,3 @@
+package packageB
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen -generate types,skip-prune --package=packageB -o externalref.gen.go spec.yaml
diff --git a/internal/test/externalref/packageB/externalref.gen.go b/internal/test/externalref/packageB/externalref.gen.go
new file mode 100644
index 0000000..831b7e8
--- /dev/null
+++ b/internal/test/externalref/packageB/externalref.gen.go
@@ -0,0 +1,9 @@
+// Package packageB provides primitives to interact the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
+package packageB
+
+// ObjectB defines model for ObjectB.
+type ObjectB struct {
+	Name *string `json:"name,omitempty"`
+}
diff --git a/internal/test/externalref/packageB/spec.yaml b/internal/test/externalref/packageB/spec.yaml
new file mode 100644
index 0000000..6f90634
--- /dev/null
+++ b/internal/test/externalref/packageB/spec.yaml
@@ -0,0 +1,6 @@
+components:
+  schemas:
+    ObjectB:
+      properties:
+        name:
+          type: string
diff --git a/internal/test/externalref/spec.yaml b/internal/test/externalref/spec.yaml
new file mode 100644
index 0000000..455ee66
--- /dev/null
+++ b/internal/test/externalref/spec.yaml
@@ -0,0 +1,11 @@
+openapi: "3.0.0"
+info: {}
+paths: {}
+components:
+  schemas:
+    Container:
+      properties:
+        object_a:
+          $ref: ./packageA/spec.yaml#/components/schemas/ObjectA
+        object_b:
+          $ref: ./packageB/spec.yaml#/components/schemas/ObjectB
diff --git a/internal/test/issues/issue-grab_import_names/doc.go b/internal/test/issues/issue-grab_import_names/doc.go
new file mode 100644
index 0000000..2f8ae67
--- /dev/null
+++ b/internal/test/issues/issue-grab_import_names/doc.go
@@ -0,0 +1,3 @@
+package grab_import_names
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=grab_import_names -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-grab_import_names/issue.gen.go b/internal/test/issues/issue-grab_import_names/issue.gen.go
new file mode 100644
index 0000000..b03d7d0
--- /dev/null
+++ b/internal/test/issues/issue-grab_import_names/issue.gen.go
@@ -0,0 +1,376 @@
+// Package grab_import_names provides primitives to interact the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
+package grab_import_names
+
+import (
+	"bytes"
+	"compress/gzip"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/labstack/echo/v4"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+// GetFooParams defines parameters for GetFoo.
+type GetFooParams struct {
+
+	// base64. bytes. chi. context. echo. errors. fmt. gzip. http. io. ioutil. json. openapi3.
+	Foo *string `json:"Foo,omitempty"`
+
+	// openapi_types. path. runtime. strings. time.Duration time.Time url. xml. yaml.
+	Bar *string `json:"Bar,omitempty"`
+}
+
+// RequestEditorFn  is the function signature for the RequestEditor callback function
+type RequestEditorFn func(ctx context.Context, req *http.Request) error
+
+// Doer performs HTTP requests.
+//
+// The standard http.Client implements this interface.
+type HttpRequestDoer interface {
+	Do(req *http.Request) (*http.Response, error)
+}
+
+// Client which conforms to the OpenAPI3 specification for this service.
+type Client struct {
+	// The endpoint of the server conforming to this interface, with scheme,
+	// https://api.deepmap.com for example.
+	Server string
+
+	// Doer for performing requests, typically a *http.Client with any
+	// customized settings, such as certificate chains.
+	Client HttpRequestDoer
+
+	// A callback for modifying requests which are generated before sending over
+	// the network.
+	RequestEditor RequestEditorFn
+}
+
+// ClientOption allows setting custom parameters during construction
+type ClientOption func(*Client) error
+
+// Creates a new Client, with reasonable defaults
+func NewClient(server string, opts ...ClientOption) (*Client, error) {
+	// create a client with sane default values
+	client := Client{
+		Server: server,
+	}
+	// mutate client and add all optional params
+	for _, o := range opts {
+		if err := o(&client); err != nil {
+			return nil, err
+		}
+	}
+	// ensure the server URL always has a trailing slash
+	if !strings.HasSuffix(client.Server, "/") {
+		client.Server += "/"
+	}
+	// create httpClient, if not already present
+	if client.Client == nil {
+		client.Client = http.DefaultClient
+	}
+	return &client, nil
+}
+
+// WithHTTPClient allows overriding the default Doer, which is
+// automatically created using http.Client. This is useful for tests.
+func WithHTTPClient(doer HttpRequestDoer) ClientOption {
+	return func(c *Client) error {
+		c.Client = doer
+		return nil
+	}
+}
+
+// WithRequestEditorFn allows setting up a callback function, which will be
+// called right before sending the request. This can be used to mutate the request.
+func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
+	return func(c *Client) error {
+		c.RequestEditor = fn
+		return nil
+	}
+}
+
+// The interface specification for the client above.
+type ClientInterface interface {
+	// GetFoo request
+	GetFoo(ctx context.Context, params *GetFooParams) (*http.Response, error)
+}
+
+func (c *Client) GetFoo(ctx context.Context, params *GetFooParams) (*http.Response, error) {
+	req, err := NewGetFooRequest(c.Server, params)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if c.RequestEditor != nil {
+		err = c.RequestEditor(ctx, req)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return c.Client.Do(req)
+}
+
+// NewGetFooRequest generates requests for GetFoo
+func NewGetFooRequest(server string, params *GetFooParams) (*http.Request, error) {
+	var err error
+
+	queryUrl, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
+
+	basePath := fmt.Sprintf("/foo")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("GET", queryUrl.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	if params.Foo != nil {
+		var headerParam0 string
+
+		headerParam0, err = runtime.StyleParam("simple", false, "Foo", *params.Foo)
+		if err != nil {
+			return nil, err
+		}
+
+		req.Header.Add("Foo", headerParam0)
+	}
+
+	if params.Bar != nil {
+		var headerParam1 string
+
+		headerParam1, err = runtime.StyleParam("simple", false, "Bar", *params.Bar)
+		if err != nil {
+			return nil, err
+		}
+
+		req.Header.Add("Bar", headerParam1)
+	}
+
+	return req, nil
+}
+
+// ClientWithResponses builds on ClientInterface to offer response payloads
+type ClientWithResponses struct {
+	ClientInterface
+}
+
+// NewClientWithResponses creates a new ClientWithResponses, which wraps
+// Client with return type handling
+func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
+	client, err := NewClient(server, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return &ClientWithResponses{client}, nil
+}
+
+// WithBaseURL overrides the baseURL.
+func WithBaseURL(baseURL string) ClientOption {
+	return func(c *Client) error {
+		newBaseURL, err := url.Parse(baseURL)
+		if err != nil {
+			return err
+		}
+		c.Server = newBaseURL.String()
+		return nil
+	}
+}
+
+// ClientWithResponsesInterface is the interface specification for the client with responses above.
+type ClientWithResponsesInterface interface {
+	// GetFoo request
+	GetFooWithResponse(ctx context.Context, params *GetFooParams) (*GetFooResponse, error)
+}
+
+type GetFooResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *string
+}
+
+// Status returns HTTPResponse.Status
+func (r GetFooResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r GetFooResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+// GetFooWithResponse request returning *GetFooResponse
+func (c *ClientWithResponses) GetFooWithResponse(ctx context.Context, params *GetFooParams) (*GetFooResponse, error) {
+	rsp, err := c.GetFoo(ctx, params)
+	if err != nil {
+		return nil, err
+	}
+	return ParseGetFooResponse(rsp)
+}
+
+// ParseGetFooResponse parses an HTTP response from a GetFooWithResponse call
+func ParseGetFooResponse(rsp *http.Response) (*GetFooResponse, error) {
+	bodyBytes, err := ioutil.ReadAll(rsp.Body)
+	defer rsp.Body.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &GetFooResponse{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest string
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON200 = &dest
+
+	}
+
+	return response, nil
+}
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+	// (GET /foo)
+	GetFoo(ctx echo.Context, params GetFooParams) error
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+// GetFoo converts echo context to params.
+func (w *ServerInterfaceWrapper) GetFoo(ctx echo.Context) error {
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params GetFooParams
+
+	headers := ctx.Request().Header
+	// ------------- Optional header parameter "Foo" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("Foo")]; found {
+		var Foo string
+		n := len(valueList)
+		if n != 1 {
+			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Foo, got %d", n))
+		}
+
+		err = runtime.BindStyledParameter("simple", false, "Foo", valueList[0], &Foo)
+		if err != nil {
+			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter Foo: %s", err))
+		}
+
+		params.Foo = &Foo
+	}
+	// ------------- Optional header parameter "Bar" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("Bar")]; found {
+		var Bar string
+		n := len(valueList)
+		if n != 1 {
+			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Bar, got %d", n))
+		}
+
+		err = runtime.BindStyledParameter("simple", false, "Bar", valueList[0], &Bar)
+		if err != nil {
+			return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter Bar: %s", err))
+		}
+
+		params.Bar = &Bar
+	}
+
+	// Invoke the callback with all the unmarshalled arguments
+	err = w.Handler.GetFoo(ctx, params)
+	return err
+}
+
+// This is a simple interface which specifies echo.Route addition functions which
+// are present on both echo.Echo and echo.Group, since we want to allow using
+// either of them for path registration
+type EchoRouter interface {
+	CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+}
+
+// RegisterHandlers adds each server route to the EchoRouter.
+func RegisterHandlers(router EchoRouter, si ServerInterface) {
+
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	router.GET("/foo", wrapper.GetFoo)
+
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/3yRwWojMQyGX0XobJQhWfYwx2XZpffeSinORMm4jC1jaUrSMO9e7MkpNDUYIWF9/vXr",
+	"ioPELImTKfbXxWFIR8H+ihZsYuyRiNDhBxcNkrDHjjrqcHEomZPPAXvcUUdbdJi9jZWCm6M0xomthgPr",
+	"UEK2FbACJXPxtfJ0wB7/s/0TaYjiIxsXxf7lvnPvlX//IthfjJVgGAPBIMn4bAQ8jELApUhRgmM0gtNn",
+	"yASjWSYIUu9sYSJ4V0kEN/27qiZU/Mj+wAUdJh/r5KsiHUaOvjlyybWsVkI64bK4e3034lt9qATVDoIy",
+	"JwuRCdY+JWjp33kdf82eQ2SYy0RwjhPBxcfpoaw/vvwo69VhYc2SlNsytl1XQzMqtX34nKcwtO831Yta",
+	"e8xb3DcLXNr5CgAA//+IR/EuPgIAAA==",
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file.
+func GetSwagger() (*openapi3.Swagger, error) {
+	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+	if err != nil {
+		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+
+	swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes())
+	if err != nil {
+		return nil, fmt.Errorf("error loading Swagger: %s", err)
+	}
+	return swagger, nil
+}
diff --git a/internal/test/issues/issue-grab_import_names/issue_test.go b/internal/test/issues/issue-grab_import_names/issue_test.go
new file mode 100644
index 0000000..eeda4cc
--- /dev/null
+++ b/internal/test/issues/issue-grab_import_names/issue_test.go
@@ -0,0 +1,25 @@
+package grab_import_names
+
+import (
+	"testing"
+
+	"github.com/deepmap/oapi-codegen/pkg/codegen"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/stretchr/testify/require"
+)
+
+func TestLineComments(t *testing.T) {
+	swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("spec.yaml")
+	require.NoError(t, err)
+
+	opts := codegen.Options{
+		GenerateClient:     true,
+		GenerateEchoServer: true,
+		GenerateTypes:      true,
+		EmbedSpec:          true,
+	}
+
+	code, err := codegen.Generate(swagger, "grab_import_names", opts)
+	require.NoError(t, err)
+	require.NotContains(t, code, `"openapi_types"`)
+}
diff --git a/internal/test/issues/issue-grab_import_names/spec.yaml b/internal/test/issues/issue-grab_import_names/spec.yaml
new file mode 100644
index 0000000..275061e
--- /dev/null
+++ b/internal/test/issues/issue-grab_import_names/spec.yaml
@@ -0,0 +1,30 @@
+openapi: 3.0.2
+
+info:
+  title: ...
+  version: 0.0.0
+
+paths:
+  /foo:
+    get:
+      parameters:
+        - name: Foo
+          in: header
+          description: base64. bytes. chi. context. echo. errors. fmt. gzip. http. io. ioutil. json. openapi3.
+          schema:
+            type: string
+          required: false
+        - name: Bar
+          in: header
+          description: openapi_types. path. runtime. strings. time.Duration time.Time url. xml. yaml.
+          schema:
+            type: string
+          required: false
+      description: ...
+      responses:
+        200:
+          description: ...
+          content:
+            application/json:
+              schema:
+                type: string
\ No newline at end of file
diff --git a/internal/test/issues/issue-illegal_enum_names/doc.go b/internal/test/issues/issue-illegal_enum_names/doc.go
new file mode 100644
index 0000000..b348a97
--- /dev/null
+++ b/internal/test/issues/issue-illegal_enum_names/doc.go
@@ -0,0 +1,3 @@
+package illegal_enum_names
+
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen  --package=illegal_enum_names -o issue.gen.go spec.yaml
diff --git a/internal/test/issues/issue-illegal_enum_names/issue.gen.go b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
new file mode 100644
index 0000000..05ea488
--- /dev/null
+++ b/internal/test/issues/issue-illegal_enum_names/issue.gen.go
@@ -0,0 +1,322 @@
+// Package illegal_enum_names provides primitives to interact the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen DO NOT EDIT.
+package illegal_enum_names
+
+import (
+	"bytes"
+	"compress/gzip"
+	"context"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/labstack/echo/v4"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strings"
+)
+
+// Bar defines model for Bar.
+type Bar string
+
+// List of Bar
+const (
+	Bar_Bar      Bar = "Bar"
+	Bar_Foo      Bar = "Foo"
+	Bar_Foo_Bar  Bar = "Foo Bar"
+	Bar_Foo_Bar1 Bar = "Foo-Bar"
+	Bar__Foo     Bar = "1Foo"
+	Bar__Foo1    Bar = " Foo"
+	Bar__Foo_    Bar = " Foo "
+	Bar__Foo_1   Bar = "_Foo_"
+)
+
+// RequestEditorFn  is the function signature for the RequestEditor callback function
+type RequestEditorFn func(ctx context.Context, req *http.Request) error
+
+// Doer performs HTTP requests.
+//
+// The standard http.Client implements this interface.
+type HttpRequestDoer interface {
+	Do(req *http.Request) (*http.Response, error)
+}
+
+// Client which conforms to the OpenAPI3 specification for this service.
+type Client struct {
+	// The endpoint of the server conforming to this interface, with scheme,
+	// https://api.deepmap.com for example.
+	Server string
+
+	// Doer for performing requests, typically a *http.Client with any
+	// customized settings, such as certificate chains.
+	Client HttpRequestDoer
+
+	// A callback for modifying requests which are generated before sending over
+	// the network.
+	RequestEditor RequestEditorFn
+}
+
+// ClientOption allows setting custom parameters during construction
+type ClientOption func(*Client) error
+
+// Creates a new Client, with reasonable defaults
+func NewClient(server string, opts ...ClientOption) (*Client, error) {
+	// create a client with sane default values
+	client := Client{
+		Server: server,
+	}
+	// mutate client and add all optional params
+	for _, o := range opts {
+		if err := o(&client); err != nil {
+			return nil, err
+		}
+	}
+	// ensure the server URL always has a trailing slash
+	if !strings.HasSuffix(client.Server, "/") {
+		client.Server += "/"
+	}
+	// create httpClient, if not already present
+	if client.Client == nil {
+		client.Client = http.DefaultClient
+	}
+	return &client, nil
+}
+
+// WithHTTPClient allows overriding the default Doer, which is
+// automatically created using http.Client. This is useful for tests.
+func WithHTTPClient(doer HttpRequestDoer) ClientOption {
+	return func(c *Client) error {
+		c.Client = doer
+		return nil
+	}
+}
+
+// WithRequestEditorFn allows setting up a callback function, which will be
+// called right before sending the request. This can be used to mutate the request.
+func WithRequestEditorFn(fn RequestEditorFn) ClientOption {
+	return func(c *Client) error {
+		c.RequestEditor = fn
+		return nil
+	}
+}
+
+// The interface specification for the client above.
+type ClientInterface interface {
+	// GetFoo request
+	GetFoo(ctx context.Context) (*http.Response, error)
+}
+
+func (c *Client) GetFoo(ctx context.Context) (*http.Response, error) {
+	req, err := NewGetFooRequest(c.Server)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if c.RequestEditor != nil {
+		err = c.RequestEditor(ctx, req)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return c.Client.Do(req)
+}
+
+// NewGetFooRequest generates requests for GetFoo
+func NewGetFooRequest(server string) (*http.Request, error) {
+	var err error
+
+	queryUrl, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
+
+	basePath := fmt.Sprintf("/foo")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("GET", queryUrl.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return req, nil
+}
+
+// ClientWithResponses builds on ClientInterface to offer response payloads
+type ClientWithResponses struct {
+	ClientInterface
+}
+
+// NewClientWithResponses creates a new ClientWithResponses, which wraps
+// Client with return type handling
+func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) {
+	client, err := NewClient(server, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return &ClientWithResponses{client}, nil
+}
+
+// WithBaseURL overrides the baseURL.
+func WithBaseURL(baseURL string) ClientOption {
+	return func(c *Client) error {
+		newBaseURL, err := url.Parse(baseURL)
+		if err != nil {
+			return err
+		}
+		c.Server = newBaseURL.String()
+		return nil
+	}
+}
+
+// ClientWithResponsesInterface is the interface specification for the client with responses above.
+type ClientWithResponsesInterface interface {
+	// GetFoo request
+	GetFooWithResponse(ctx context.Context) (*GetFooResponse, error)
+}
+
+type GetFooResponse struct {
+	Body         []byte
+	HTTPResponse *http.Response
+	JSON200      *[]Bar
+}
+
+// Status returns HTTPResponse.Status
+func (r GetFooResponse) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r GetFooResponse) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
+// GetFooWithResponse request returning *GetFooResponse
+func (c *ClientWithResponses) GetFooWithResponse(ctx context.Context) (*GetFooResponse, error) {
+	rsp, err := c.GetFoo(ctx)
+	if err != nil {
+		return nil, err
+	}
+	return ParseGetFooResponse(rsp)
+}
+
+// ParseGetFooResponse parses an HTTP response from a GetFooWithResponse call
+func ParseGetFooResponse(rsp *http.Response) (*GetFooResponse, error) {
+	bodyBytes, err := ioutil.ReadAll(rsp.Body)
+	defer rsp.Body.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &GetFooResponse{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest []Bar
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.JSON200 = &dest
+
+	}
+
+	return response, nil
+}
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+	// (GET /foo)
+	GetFoo(ctx echo.Context) error
+}
+
+// ServerInterfaceWrapper converts echo contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+// GetFoo converts echo context to params.
+func (w *ServerInterfaceWrapper) GetFoo(ctx echo.Context) error {
+	var err error
+
+	// Invoke the callback with all the unmarshalled arguments
+	err = w.Handler.GetFoo(ctx)
+	return err
+}
+
+// This is a simple interface which specifies echo.Route addition functions which
+// are present on both echo.Echo and echo.Group, since we want to allow using
+// either of them for path registration
+type EchoRouter interface {
+	CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+	TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
+}
+
+// RegisterHandlers adds each server route to the EchoRouter.
+func RegisterHandlers(router EchoRouter, si ServerInterface) {
+
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	router.GET("/foo", wrapper.GetFoo)
+
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/0yQzU4DMQyEX6UaOIbsUm45cijiGRCqoq23DeraUWKQqirvjpyFQi4z+ZnYn6+YZMnC",
+	"xFoRrqjTiZbY7XMsJsSfC8IbdiJw/dCZ39zcw+oe/z/YrBuTDRz2O5E93h30kgkBVUviI1prDolnsTqa",
+	"9Gx33ns4fFGpSRgBox/9iOYgmTjmhIAnP/otHHLUU291mKX/cSQ1kUwlahJ+PSDghXTtplDNwpV6ZDuO",
+	"JpOwEvdUzPmcpp4bPqrV/h2HuaS09OB9oRkBd8Pf4IafqQ0G326UsZR4WSEPVKeSsq5Ihtj6+g4AAP//",
+	"Jkt64H8BAAA=",
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file.
+func GetSwagger() (*openapi3.Swagger, error) {
+	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+	if err != nil {
+		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+
+	swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromData(buf.Bytes())
+	if err != nil {
+		return nil, fmt.Errorf("error loading Swagger: %s", err)
+	}
+	return swagger, nil
+}
diff --git a/internal/test/issues/issue-illegal_enum_names/issue_test.go b/internal/test/issues/issue-illegal_enum_names/issue_test.go
new file mode 100644
index 0000000..9fbd124
--- /dev/null
+++ b/internal/test/issues/issue-illegal_enum_names/issue_test.go
@@ -0,0 +1,54 @@
+package illegal_enum_names
+
+import (
+	"go/ast"
+	"go/parser"
+	"go/token"
+	"testing"
+
+	"github.com/deepmap/oapi-codegen/pkg/codegen"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/stretchr/testify/require"
+)
+
+func TestIllegalEnumNames(t *testing.T) {
+	swagger, err := openapi3.NewSwaggerLoader().LoadSwaggerFromFile("spec.yaml")
+	require.NoError(t, err)
+
+	opts := codegen.Options{
+		GenerateClient:     true,
+		GenerateEchoServer: true,
+		GenerateTypes:      true,
+		EmbedSpec:          true,
+	}
+
+	code, err := codegen.Generate(swagger, "illegal_enum_names", opts)
+	require.NoError(t, err)
+
+	f, err := parser.ParseFile(token.NewFileSet(), "", code, parser.AllErrors)
+	require.NoError(t, err)
+
+	constDefs := make(map[string]string)
+	for _, d := range f.Decls {
+		switch decl := d.(type) {
+		case *ast.GenDecl:
+			if token.CONST == decl.Tok {
+				for _, s := range decl.Specs {
+					switch spec := s.(type) {
+					case *ast.ValueSpec:
+						constDefs[spec.Names[0].Name] = spec.Names[0].Obj.Decl.(*ast.ValueSpec).Values[0].(*ast.BasicLit).Value
+					}
+				}
+			}
+		}
+	}
+
+	require.Equal(t, `"Bar"`, constDefs["Bar_Bar"])
+	require.Equal(t, `"Foo"`, constDefs["Bar_Foo"])
+	require.Equal(t, `"Foo Bar"`, constDefs["Bar_Foo_Bar"])
+	require.Equal(t, `"Foo-Bar"`, constDefs["Bar_Foo_Bar1"])
+	require.Equal(t, `"1Foo"`, constDefs["Bar__Foo"])
+	require.Equal(t, `" Foo"`, constDefs["Bar__Foo1"])
+	require.Equal(t, `" Foo "`, constDefs["Bar__Foo_"])
+	require.Equal(t, `"_Foo_"`, constDefs["Bar__Foo_1"])
+}
diff --git a/internal/test/issues/issue-illegal_enum_names/spec.yaml b/internal/test/issues/issue-illegal_enum_names/spec.yaml
new file mode 100644
index 0000000..de6cbe8
--- /dev/null
+++ b/internal/test/issues/issue-illegal_enum_names/spec.yaml
@@ -0,0 +1,33 @@
+openapi: 3.0.2
+
+info:
+  title: ...
+  version: 0.0.0
+
+paths:
+  /foo:
+    get:
+      responses:
+        200:
+          description: ...
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  $ref: '#/components/schemas/Bar'
+
+components:
+  schemas:
+    Bar:
+      type: string
+      enum: 
+        - Foo
+        - Bar
+        - Foo Bar
+        - Foo-Bar
+        - 1Foo
+        - Bar
+        - ' Foo'
+        - ' Foo '
+        - _Foo_
\ No newline at end of file
diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go
index 270471a..ee387bf 100644
--- a/internal/test/schemas/schemas.gen.go
+++ b/internal/test/schemas/schemas.gen.go
@@ -45,6 +45,9 @@ type NullableProperties struct {
 	RequiredAndNullable *string `json:"requiredAndNullable"`
 }
 
+// StringInPath defines model for StringInPath.
+type StringInPath string
+
 // Issue185JSONBody defines parameters for Issue185.
 type Issue185JSONBody NullableProperties
 
@@ -144,6 +147,9 @@ type ClientInterface interface {
 
 	Issue185(ctx context.Context, body Issue185JSONRequestBody) (*http.Response, error)
 
+	// Issue209 request
+	Issue209(ctx context.Context, str StringInPath) (*http.Response, error)
+
 	// Issue30 request
 	Issue30(ctx context.Context, pFallthrough string) (*http.Response, error)
 
@@ -216,6 +222,21 @@ func (c *Client) Issue185(ctx context.Context, body Issue185JSONRequestBody) (*h
 	return c.Client.Do(req)
 }
 
+func (c *Client) Issue209(ctx context.Context, str StringInPath) (*http.Response, error) {
+	req, err := NewIssue209Request(c.Server, str)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if c.RequestEditor != nil {
+		err = c.RequestEditor(ctx, req)
+		if err != nil {
+			return nil, err
+		}
+	}
+	return c.Client.Do(req)
+}
+
 func (c *Client) Issue30(ctx context.Context, pFallthrough string) (*http.Response, error) {
 	req, err := NewIssue30Request(c.Server, pFallthrough)
 	if err != nil {
@@ -369,6 +390,40 @@ func NewIssue185RequestWithBody(server string, contentType string, body io.Reade
 	return req, nil
 }
 
+// NewIssue209Request generates requests for Issue209
+func NewIssue209Request(server string, str StringInPath) (*http.Request, error) {
+	var err error
+
+	var pathParam0 string
+
+	pathParam0, err = runtime.StyleParam("simple", false, "str", str)
+	if err != nil {
+		return nil, err
+	}
+
+	queryUrl, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
+
+	basePath := fmt.Sprintf("/issues/209/$%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("GET", queryUrl.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return req, nil
+}
+
 // NewIssue30Request generates requests for Issue30
 func NewIssue30Request(server string, pFallthrough string) (*http.Request, error) {
 	var err error
@@ -532,6 +587,9 @@ type ClientWithResponsesInterface interface {
 
 	Issue185WithResponse(ctx context.Context, body Issue185JSONRequestBody) (*Issue185Response, error)
 
+	// Issue209 request
+	Issue209WithResponse(ctx context.Context, str StringInPath) (*Issue209Response, error)
+
 	// Issue30 request
 	Issue30WithResponse(ctx context.Context, pFallthrough string) (*Issue30Response, error)
 
@@ -618,6 +676,27 @@ func (r Issue185Response) StatusCode() int {
 	return 0
 }
 
+type Issue209Response struct {
+	Body         []byte
+	HTTPResponse *http.Response
+}
+
+// Status returns HTTPResponse.Status
+func (r Issue209Response) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r Issue209Response) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
 type Issue30Response struct {
 	Body         []byte
 	HTTPResponse *http.Response
@@ -716,6 +795,15 @@ func (c *ClientWithResponses) Issue185WithResponse(ctx context.Context, body Iss
 	return ParseIssue185Response(rsp)
 }
 
+// Issue209WithResponse request returning *Issue209Response
+func (c *ClientWithResponses) Issue209WithResponse(ctx context.Context, str StringInPath) (*Issue209Response, error) {
+	rsp, err := c.Issue209(ctx, str)
+	if err != nil {
+		return nil, err
+	}
+	return ParseIssue209Response(rsp)
+}
+
 // Issue30WithResponse request returning *Issue30Response
 func (c *ClientWithResponses) Issue30WithResponse(ctx context.Context, pFallthrough string) (*Issue30Response, error) {
 	rsp, err := c.Issue30(ctx, pFallthrough)
@@ -855,6 +943,25 @@ func ParseIssue185Response(rsp *http.Response) (*Issue185Response, error) {
 	return response, nil
 }
 
+// ParseIssue209Response parses an HTTP response from a Issue209WithResponse call
+func ParseIssue209Response(rsp *http.Response) (*Issue209Response, error) {
+	bodyBytes, err := ioutil.ReadAll(rsp.Body)
+	defer rsp.Body.Close()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &Issue209Response{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	}
+
+	return response, nil
+}
+
 // ParseIssue30Response parses an HTTP response from a Issue30WithResponse call
 func ParseIssue30Response(rsp *http.Response) (*Issue30Response, error) {
 	bodyBytes, err := ioutil.ReadAll(rsp.Body)
@@ -924,6 +1031,9 @@ type ServerInterface interface {
 	// (GET /issues/185)
 	Issue185(ctx echo.Context) error
 
+	// (GET /issues/209/${str})
+	Issue209(ctx echo.Context, str StringInPath) error
+
 	// (GET /issues/30/{fallthrough})
 	Issue30(ctx echo.Context, pFallthrough string) error
 
@@ -966,6 +1076,22 @@ func (w *ServerInterfaceWrapper) Issue185(ctx echo.Context) error {
 	return err
 }
 
+// Issue209 converts echo context to params.
+func (w *ServerInterfaceWrapper) Issue209(ctx echo.Context) error {
+	var err error
+	// ------------- Path parameter "str" -------------
+	var str StringInPath
+
+	err = runtime.BindStyledParameter("simple", false, "str", ctx.Param("str"), &str)
+	if err != nil {
+		return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter str: %s", err))
+	}
+
+	// Invoke the callback with all the unmarshalled arguments
+	err = w.Handler.Issue209(ctx, str)
+	return err
+}
+
 // Issue30 converts echo context to params.
 func (w *ServerInterfaceWrapper) Issue30(ctx echo.Context) error {
 	var err error
@@ -1041,6 +1167,7 @@ func RegisterHandlers(router EchoRouter, si ServerInterface) {
 	router.GET("/ensure-everything-is-referenced", wrapper.EnsureEverythingIsReferenced)
 	router.GET("/issues/127", wrapper.Issue127)
 	router.GET("/issues/185", wrapper.Issue185)
+	router.GET("/issues/209/$:str", wrapper.Issue209)
 	router.GET("/issues/30/:fallthrough", wrapper.Issue30)
 	router.GET("/issues/41/:1param", wrapper.Issue41)
 	router.GET("/issues/9", wrapper.Issue9)
@@ -1050,22 +1177,23 @@ func RegisterHandlers(router EchoRouter, si ServerInterface) {
 // Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
-	"H4sIAAAAAAAC/7RW32/bNhD+Vw7cgL04lp00WKu3riiGPKwNmgB7aPJAi2eLjXRkyWMSwdD/PpCyIqWW",
-	"vXVpnyxLvLvv++4Xt6IwtTWExF7kW+GLEmuZHs+vWDr2f2suP4R6hS6+VOgLpy1rQyIX16X20JkAyRrB",
-	"JxN40FyCBOrMZoIbiyIXZvUFCxbtTLyl5rqxuBT5dvh3eihAaUKlYIUgCTQxurUscNtGR++CZ1NfsdO0",
-	"uU5RtmJtXC1Z5KJIH4f4Ph2LZn8iodPFxw5Qvt1H+CFUlVxVeOmMRccakyb22T+TYMpq5GAI0X98S6r3",
-	"Fc/R0zO7gBPQHH4N2qGadNp//D6nz7x+Hp6n/d3u5Ss60LQ2E/lBz1BIjx7WxsG9dNoED9r7kF4FUmDu",
-	"0QHrGudwWaH0CFIpkMC9bTS9IUkNrMIG1voR1fyGYto0R05dlCt096mY7tH5LvpyvpgvOq2RpNUiF2fz",
-	"xXwpZsJKLlOOMiQfHJ7gPbqGS02bE+1PHK7RIRWdzBvkA6WHpKzRxICP2rMHb4BLyTA0DRSSYmkWDiWj",
-	"Ak3ApfY35C0WIEkBGY4HrAuEKvGKNSRjmAslcvE+AXz/hO/CfxrQxRR5a8h3FXe6WMSfwhAjJdDS2koX",
-	"yVv2xUfkfRPv16scuk786nAtcvFLNlDJds2fPXVnO+ttTv+jzWm0KSaa8pjtXhPHgturwTbVYdbVVrY8",
-	"/f1g6v6SdwhRVAjkg7XGxcwk0R4ZomMPytBvDNYh1pZhOJW+zifSdBHjxqgvTMkxIZ6PpUh37Ouxrl7i",
-	"KpLPaunulHmgFztq5EvQRDcK1zJU/BPF+0GMv6281+eHh0ZjETbRPjGAhxIJ+k2Q9dMWhrYE6RD68X24",
-	"7F6f74Y1ev7DqOaHiTax5jq2oxqP8MYCnC2y7VpWFZfOhE3Z7svwCX2c1grusHkwTo13uHWYRnyclHFf",
-	"xOjp8rDrup2uEyqcLcQ+rjjqnayR0XmRf94KHQHE8S9mIroVuRiBFeNV2C3LQahv1+btiPSrZbZdplDt",
-	"wexf9khGlyFNm+469HQZmmD2qltZ/8aji3+UwrFc71/o2vb2aKbfHKT6rtJIDAmQT0MTNBXGOSy4auJz",
-	"FRSqdCvY1W0nw8qoJq7FGxr4Hqz7Nwdk+RrQNaP8GvN9ef3fvbQbXGMlPu66OzETU53TzkRqhx2D4CqR",
-	"i5LZ5lm2u7nEu9BcIdpa2rnUor1t/wkAAP//DG3tepoLAAA=",
+	"H4sIAAAAAAAC/7RX32/bNhD+Vw5cgb24lu00WKO3rCiGPKwNmgB7aPJAi2eLjUSy5CmJYOh/H46yLXmW",
+	"vGbpniKZuh/fd98dLxuR2dJZg4aCSDfCSS9LJPTx7Ya8Nusrcy0p53eFIfPakbZGpOISQjwHJymHvaWY",
+	"CM3H/KuYCCNLFKkIxAcev1faoxIp+QonImQ5lpJdU+22n2mzFk3T7A5jIuc3JD2FvzTln6pyif44m9tc",
+	"B2hNgGNCiCbwpCkHCaY1m+wC2eU3zEg0E3Fp6tva4Vykm+5tMRYgt1WhYIkgDWhD6Fcyw03Djj5UgWzZ",
+	"cnYbo2zEyvpSkkhFFg+7+DugE/EHGvQ6+9wm1FHRZfipKgq5LPDaW4eeNLalOnizMU1ZDHA52R9eGrXz",
+	"xd+Z/XNbjSO7rlyb8cOXOT3w+rV7HvZ3f1QvdqDNyg7UBwNBJgMGWFkPj9JrWwXQIVTxp8oosI/ogXSJ",
+	"U7guUAYEqRRIoJ0tm94ZaWpYVmtY6WdU0zvDZdPEmNooN+gfo5ge0Yc2+nw6m85artFIp0Uqzqaz6VxM",
+	"YiPEGiVoQuXxLT6irynXZv1Wh7ceV+jRZC3Na6QR6aFRzmpDgM86UIBggXJJ0DUwZNKwNDOPklCBNkC5",
+	"DncmOMxAGgXGEn/gfGVQRVysIclhrpRIxceY4Md9flfhS5cdlyg4a0KruMVsxn8yawhNTFo6V+gseku+",
+	"Bc580+vwQ73KruvEG48rkYpfkg5Ksm3+ZN+dzWRns/hBmwXbZANNecr2qIlZcEcabKIOk1ZbyXzx22jp",
+	"/pQPCEwqVCZUzlnPlYmkPROw4wDKml8JnEcsHUH3VTydDpTpiuNy1FeW5BQRh2OJ4fZ9PZfFa1wx+KSU",
+	"/kHZJ/NqR7V8TTbsRuFKVgX9j+T9JMT/VN778/GhUTuENdtHBPCUo4HdTZDspi10bQnSI+zG97js3p9v",
+	"hzUG+t2q+qeRNnDNtWh7Guf0+gQsZhfJm00g34zy8CHH7CGAXnUrSgtVYVbIjoKiHga8mF2I4xwmB6vS",
+	"12Fk3SfJwSrV3PcgnM2SzUoWBeXeVuu8OUbwBQNfOAoesH6yXvXXEOcx3lI87PnKYwLj/rMdHFtKBnCd",
+	"zX4E1sAq10v2RStdH/S7ebKZx1DjhbveZdLb53jdjBvdfp8bQPauvXX/DUcb/ySEU3I93kmb5v6kWC/G",
+	"NVpoNNQKNMS5D9pk1nvMqKj5uagUqrjYbFuvpWFpVc03+53p8I627sUILd8r9HWvvta+rK7/eRxsZ2+f",
+	"ic/bARWRiaHm5/8M4v7VIqh8IVKRE7k0SbbLF69zU4XoSummUnO//R0AAP//Wanza+kMAAA=",
 }
 
 // GetSwagger returns the Swagger specification corresponding to the generated code
diff --git a/internal/test/schemas/schemas.yaml b/internal/test/schemas/schemas.yaml
index 4c2e375..3efb89d 100644
--- a/internal/test/schemas/schemas.yaml
+++ b/internal/test/schemas/schemas.yaml
@@ -104,6 +104,14 @@ paths:
           application/json:
             schema:
               $ref: "#/components/schemas/NullableProperties"
+  /issues/209/${str}:
+    parameters:
+      - $ref: '#/components/parameters/StringInPath'
+    get:
+      operationId: Issue209
+      description: Checks if parameters are declared properly
+
+
 components:
   schemas:
     GenericObject:
@@ -133,3 +141,11 @@ components:
     5StartsWithNumber:
       type: object
       description: This schema name starts with a number
+  parameters:
+    StringInPath:
+      name: str
+      description: A string path parameter
+      in: path
+      required: true
+      schema:
+        type: string
diff --git a/internal/test/server/server.gen.go b/internal/test/server/server.gen.go
index b4f50e6..26e4923 100644
--- a/internal/test/server/server.gen.go
+++ b/internal/test/server/server.gen.go
@@ -4,7 +4,6 @@
 package server
 
 import (
-	"context"
 	"fmt"
 	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	openapi_types "github.com/deepmap/oapi-codegen/pkg/types"
@@ -37,14 +36,15 @@ type EveryTypeOptional struct {
 
 // EveryTypeRequired defines model for EveryTypeRequired.
 type EveryTypeRequired struct {
-	ArrayInlineField     []int              `json:"array_inline_field"`
-	ArrayReferencedField []SomeObject       `json:"array_referenced_field"`
-	BoolField            bool               `json:"bool_field"`
-	ByteField            []byte             `json:"byte_field"`
-	DateField            openapi_types.Date `json:"date_field"`
-	DateTimeField        time.Time          `json:"date_time_field"`
-	DoubleField          float64            `json:"double_field"`
-	FloatField           float32            `json:"float_field"`
+	ArrayInlineField     []int                `json:"array_inline_field"`
+	ArrayReferencedField []SomeObject         `json:"array_referenced_field"`
+	BoolField            bool                 `json:"bool_field"`
+	ByteField            []byte               `json:"byte_field"`
+	DateField            openapi_types.Date   `json:"date_field"`
+	DateTimeField        time.Time            `json:"date_time_field"`
+	DoubleField          float64              `json:"double_field"`
+	EmailField           *openapi_types.Email `json:"email_field,omitempty"`
+	FloatField           float32              `json:"float_field"`
 	InlineObjectField    struct {
 		Name   string `json:"name"`
 		Number int    `json:"number"`
@@ -125,271 +125,243 @@ type CreateResource2JSONRequestBody CreateResource2JSONBody
 // UpdateResource3RequestBody defines body for UpdateResource3 for application/json ContentType.
 type UpdateResource3JSONRequestBody UpdateResource3JSONBody
 
+// ServerInterface represents all server handlers.
 type ServerInterface interface {
-	// get every type optional (GET /every-type-optional)
+	// get every type optional
+	// (GET /every-type-optional)
 	GetEveryTypeOptional(w http.ResponseWriter, r *http.Request)
-	// Get resource via simple path (GET /get-simple)
+	// Get resource via simple path
+	// (GET /get-simple)
 	GetSimple(w http.ResponseWriter, r *http.Request)
-	// Getter with referenced parameter and referenced response (GET /get-with-args)
-	GetWithArgs(w http.ResponseWriter, r *http.Request)
-	// Getter with referenced parameter and referenced response (GET /get-with-references/{global_argument}/{argument})
-	GetWithReferences(w http.ResponseWriter, r *http.Request)
-	// Get an object by ID (GET /get-with-type/{content_type})
-	GetWithContentType(w http.ResponseWriter, r *http.Request)
-	// get with reserved keyword (GET /reserved-keyword)
+	// Getter with referenced parameter and referenced response
+	// (GET /get-with-args)
+	GetWithArgs(w http.ResponseWriter, r *http.Request, params GetWithArgsParams)
+	// Getter with referenced parameter and referenced response
+	// (GET /get-with-references/{global_argument}/{argument})
+	GetWithReferences(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument)
+	// Get an object by ID
+	// (GET /get-with-type/{content_type})
+	GetWithContentType(w http.ResponseWriter, r *http.Request, contentType string)
+	// get with reserved keyword
+	// (GET /reserved-keyword)
 	GetReservedKeyword(w http.ResponseWriter, r *http.Request)
-	// Create a resource (POST /resource/{argument})
-	CreateResource(w http.ResponseWriter, r *http.Request)
-	// Create a resource with inline parameter (POST /resource2/{inline_argument})
-	CreateResource2(w http.ResponseWriter, r *http.Request)
-	// Update a resource with inline body. The parameter name is a reservedkeyword, so make sure that gets prefixed to avoid syntax errors (PUT /resource3/{fallthrough})
-	UpdateResource3(w http.ResponseWriter, r *http.Request)
-	// get response with reference (GET /response-with-reference)
+	// Create a resource
+	// (POST /resource/{argument})
+	CreateResource(w http.ResponseWriter, r *http.Request, argument Argument)
+	// Create a resource with inline parameter
+	// (POST /resource2/{inline_argument})
+	CreateResource2(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params)
+	// Update a resource with inline body. The parameter name is a reserved
+	// keyword, so make sure that gets prefixed to avoid syntax errors
+	// (PUT /resource3/{fallthrough})
+	UpdateResource3(w http.ResponseWriter, r *http.Request, pFallthrough int)
+	// get response with reference
+	// (GET /response-with-reference)
 	GetResponseWithReference(w http.ResponseWriter, r *http.Request)
 }
 
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
 // GetEveryTypeOptional operation middleware
-func GetEveryTypeOptionalCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetEveryTypeOptional(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetEveryTypeOptional(w, r.WithContext(ctx))
 }
 
 // GetSimple operation middleware
-func GetSimpleCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
-
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
-}
+func (siw *ServerInterfaceWrapper) GetSimple(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-// ParamsForGetWithArgs operation parameters from context
-func ParamsForGetWithArgs(ctx context.Context) *GetWithArgsParams {
-	return ctx.Value("GetWithArgsParams").(*GetWithArgsParams)
+	siw.Handler.GetSimple(w, r.WithContext(ctx))
 }
 
 // GetWithArgs operation middleware
-func GetWithArgsCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetWithArgs(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// Parameter object where we will unmarshal all parameters from the context
-		var params GetWithArgsParams
+	// Parameter object where we will unmarshal all parameters from the context
+	var params GetWithArgsParams
 
-		// ------------- Optional query parameter "optional_argument" -------------
-		if paramValue := r.URL.Query().Get("optional_argument"); paramValue != "" {
+	// ------------- Optional query parameter "optional_argument" -------------
+	if paramValue := r.URL.Query().Get("optional_argument"); paramValue != "" {
 
-		}
+	}
 
-		err = runtime.BindQueryParameter("form", true, false, "optional_argument", r.URL.Query(), &params.OptionalArgument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter optional_argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	err = runtime.BindQueryParameter("form", true, false, "optional_argument", r.URL.Query(), &params.OptionalArgument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter optional_argument: %s", err), http.StatusBadRequest)
+		return
+	}
+
+	// ------------- Required query parameter "required_argument" -------------
+	if paramValue := r.URL.Query().Get("required_argument"); paramValue != "" {
+
+	} else {
+		http.Error(w, "Query argument required_argument is required, but not found", http.StatusBadRequest)
+		return
+	}
 
-		// ------------- Required query parameter "required_argument" -------------
-		if paramValue := r.URL.Query().Get("required_argument"); paramValue != "" {
+	err = runtime.BindQueryParameter("form", true, true, "required_argument", r.URL.Query(), &params.RequiredArgument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter required_argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		} else {
-			http.Error(w, "Query argument required_argument is required, but not found", http.StatusBadRequest)
+	headers := r.Header
+
+	// ------------- Optional header parameter "header_argument" -------------
+	if valueList, found := headers[http.CanonicalHeaderKey("header_argument")]; found {
+		var HeaderArgument int32
+		n := len(valueList)
+		if n != 1 {
+			http.Error(w, fmt.Sprintf("Expected one value for header_argument, got %d", n), http.StatusBadRequest)
 			return
 		}
 
-		err = runtime.BindQueryParameter("form", true, true, "required_argument", r.URL.Query(), &params.RequiredArgument)
+		err = runtime.BindStyledParameter("simple", false, "header_argument", valueList[0], &HeaderArgument)
 		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter required_argument: %s", err), http.StatusBadRequest)
+			http.Error(w, fmt.Sprintf("Invalid format for parameter header_argument: %s", err), http.StatusBadRequest)
 			return
 		}
 
-		headers := r.Header
-
-		// ------------- Optional header parameter "header_argument" -------------
-		if valueList, found := headers[http.CanonicalHeaderKey("header_argument")]; found {
-			var HeaderArgument int32
-			n := len(valueList)
-			if n != 1 {
-				http.Error(w, fmt.Sprintf("Expected one value for header_argument, got %d", n), http.StatusBadRequest)
-				return
-			}
-
-			err = runtime.BindStyledParameter("simple", false, "header_argument", valueList[0], &HeaderArgument)
-			if err != nil {
-				http.Error(w, fmt.Sprintf("Invalid format for parameter header_argument: %s", err), http.StatusBadRequest)
-				return
-			}
+		params.HeaderArgument = &HeaderArgument
 
-			params.HeaderArgument = &HeaderArgument
+	}
 
-		}
-
-		ctx = context.WithValue(ctx, "GetWithArgsParams", &params)
-
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetWithArgs(w, r.WithContext(ctx), params)
 }
 
 // GetWithReferences operation middleware
-func GetWithReferencesCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// ------------- Path parameter "global_argument" -------------
-		var globalArgument int64
-
-		err = runtime.BindStyledParameter("simple", false, "global_argument", chi.URLParam(r, "global_argument"), &globalArgument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter global_argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	// ------------- Path parameter "global_argument" -------------
+	var globalArgument int64
 
-		ctx = context.WithValue(ctx, "globalArgument", globalArgument)
-		// ------------- Path parameter "argument" -------------
-		var argument Argument
+	err = runtime.BindStyledParameter("simple", false, "global_argument", chi.URLParam(r, "global_argument"), &globalArgument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter global_argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	// ------------- Path parameter "argument" -------------
+	var argument Argument
 
-		ctx = context.WithValue(ctx, "argument", argument)
+	err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetWithReferences(w, r.WithContext(ctx), globalArgument, argument)
 }
 
 // GetWithContentType operation middleware
-func GetWithContentTypeCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetWithContentType(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// ------------- Path parameter "content_type" -------------
-		var contentType string
+	// ------------- Path parameter "content_type" -------------
+	var contentType string
 
-		err = runtime.BindStyledParameter("simple", false, "content_type", chi.URLParam(r, "content_type"), &contentType)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter content_type: %s", err), http.StatusBadRequest)
-			return
-		}
-
-		ctx = context.WithValue(ctx, "contentType", contentType)
+	err = runtime.BindStyledParameter("simple", false, "content_type", chi.URLParam(r, "content_type"), &contentType)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter content_type: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetWithContentType(w, r.WithContext(ctx), contentType)
 }
 
 // GetReservedKeyword operation middleware
-func GetReservedKeywordCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetReservedKeyword(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetReservedKeyword(w, r.WithContext(ctx))
 }
 
 // CreateResource operation middleware
-func CreateResourceCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) CreateResource(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
-
-		// ------------- Path parameter "argument" -------------
-		var argument Argument
-
-		err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	var err error
 
-		ctx = context.WithValue(ctx, "argument", argument)
+	// ------------- Path parameter "argument" -------------
+	var argument Argument
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
-}
+	err = runtime.BindStyledParameter("simple", false, "argument", chi.URLParam(r, "argument"), &argument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-// ParamsForCreateResource2 operation parameters from context
-func ParamsForCreateResource2(ctx context.Context) *CreateResource2Params {
-	return ctx.Value("CreateResource2Params").(*CreateResource2Params)
+	siw.Handler.CreateResource(w, r.WithContext(ctx), argument)
 }
 
 // CreateResource2 operation middleware
-func CreateResource2Ctx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) CreateResource2(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
-
-		// ------------- Path parameter "inline_argument" -------------
-		var inlineArgument int
-
-		err = runtime.BindStyledParameter("simple", false, "inline_argument", chi.URLParam(r, "inline_argument"), &inlineArgument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter inline_argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	var err error
 
-		ctx = context.WithValue(ctx, "inlineArgument", inlineArgument)
+	// ------------- Path parameter "inline_argument" -------------
+	var inlineArgument int
 
-		// Parameter object where we will unmarshal all parameters from the context
-		var params CreateResource2Params
+	err = runtime.BindStyledParameter("simple", false, "inline_argument", chi.URLParam(r, "inline_argument"), &inlineArgument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter inline_argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		// ------------- Optional query parameter "inline_query_argument" -------------
-		if paramValue := r.URL.Query().Get("inline_query_argument"); paramValue != "" {
+	// Parameter object where we will unmarshal all parameters from the context
+	var params CreateResource2Params
 
-		}
+	// ------------- Optional query parameter "inline_query_argument" -------------
+	if paramValue := r.URL.Query().Get("inline_query_argument"); paramValue != "" {
 
-		err = runtime.BindQueryParameter("form", true, false, "inline_query_argument", r.URL.Query(), &params.InlineQueryArgument)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter inline_query_argument: %s", err), http.StatusBadRequest)
-			return
-		}
+	}
 
-		ctx = context.WithValue(ctx, "CreateResource2Params", &params)
+	err = runtime.BindQueryParameter("form", true, false, "inline_query_argument", r.URL.Query(), &params.InlineQueryArgument)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter inline_query_argument: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.CreateResource2(w, r.WithContext(ctx), inlineArgument, params)
 }
 
 // UpdateResource3 operation middleware
-func UpdateResource3Ctx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) UpdateResource3(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		var err error
+	var err error
 
-		// ------------- Path parameter "fallthrough" -------------
-		var pFallthrough int
+	// ------------- Path parameter "fallthrough" -------------
+	var pFallthrough int
 
-		err = runtime.BindStyledParameter("simple", false, "fallthrough", chi.URLParam(r, "fallthrough"), &pFallthrough)
-		if err != nil {
-			http.Error(w, fmt.Sprintf("Invalid format for parameter fallthrough: %s", err), http.StatusBadRequest)
-			return
-		}
-
-		ctx = context.WithValue(ctx, "pFallthrough", pFallthrough)
+	err = runtime.BindStyledParameter("simple", false, "fallthrough", chi.URLParam(r, "fallthrough"), &pFallthrough)
+	if err != nil {
+		http.Error(w, fmt.Sprintf("Invalid format for parameter fallthrough: %s", err), http.StatusBadRequest)
+		return
+	}
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.UpdateResource3(w, r.WithContext(ctx), pFallthrough)
 }
 
 // GetResponseWithReference operation middleware
-func GetResponseWithReferenceCtx(next http.Handler) http.Handler {
-	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		ctx := r.Context()
+func (siw *ServerInterfaceWrapper) GetResponseWithReference(w http.ResponseWriter, r *http.Request) {
+	ctx := r.Context()
 
-		next.ServeHTTP(w, r.WithContext(ctx))
-	})
+	siw.Handler.GetResponseWithReference(w, r.WithContext(ctx))
 }
 
 // Handler creates http.Handler with routing matching OpenAPI spec.
@@ -399,45 +371,39 @@ func Handler(si ServerInterface) http.Handler {
 
 // HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
 func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
 	r.Group(func(r chi.Router) {
-		r.Use(GetEveryTypeOptionalCtx)
-		r.Get("/every-type-optional", si.GetEveryTypeOptional)
+		r.Get("/every-type-optional", wrapper.GetEveryTypeOptional)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetSimpleCtx)
-		r.Get("/get-simple", si.GetSimple)
+		r.Get("/get-simple", wrapper.GetSimple)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetWithArgsCtx)
-		r.Get("/get-with-args", si.GetWithArgs)
+		r.Get("/get-with-args", wrapper.GetWithArgs)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetWithReferencesCtx)
-		r.Get("/get-with-references/{global_argument}/{argument}", si.GetWithReferences)
+		r.Get("/get-with-references/{global_argument}/{argument}", wrapper.GetWithReferences)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetWithContentTypeCtx)
-		r.Get("/get-with-type/{content_type}", si.GetWithContentType)
+		r.Get("/get-with-type/{content_type}", wrapper.GetWithContentType)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetReservedKeywordCtx)
-		r.Get("/reserved-keyword", si.GetReservedKeyword)
+		r.Get("/reserved-keyword", wrapper.GetReservedKeyword)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(CreateResourceCtx)
-		r.Post("/resource/{argument}", si.CreateResource)
+		r.Post("/resource/{argument}", wrapper.CreateResource)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(CreateResource2Ctx)
-		r.Post("/resource2/{inline_argument}", si.CreateResource2)
+		r.Post("/resource2/{inline_argument}", wrapper.CreateResource2)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(UpdateResource3Ctx)
-		r.Put("/resource3/{fallthrough}", si.UpdateResource3)
+		r.Put("/resource3/{fallthrough}", wrapper.UpdateResource3)
 	})
 	r.Group(func(r chi.Router) {
-		r.Use(GetResponseWithReferenceCtx)
-		r.Get("/response-with-reference", si.GetResponseWithReference)
+		r.Get("/response-with-reference", wrapper.GetResponseWithReference)
 	})
 
 	return r
diff --git a/internal/test/server/server_moq.gen.go b/internal/test/server/server_moq.gen.go
index 0736acc..b3194e7 100644
--- a/internal/test/server/server_moq.gen.go
+++ b/internal/test/server/server_moq.gen.go
@@ -31,10 +31,10 @@ var _ ServerInterface = &ServerInterfaceMock{}
 //
 //         // make and configure a mocked ServerInterface
 //         mockedServerInterface := &ServerInterfaceMock{
-//             CreateResourceFunc: func(w http.ResponseWriter, r *http.Request)  {
+//             CreateResourceFunc: func(w http.ResponseWriter, r *http.Request, argument Argument)  {
 // 	               panic("mock out the CreateResource method")
 //             },
-//             CreateResource2Func: func(w http.ResponseWriter, r *http.Request)  {
+//             CreateResource2Func: func(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params)  {
 // 	               panic("mock out the CreateResource2 method")
 //             },
 //             GetEveryTypeOptionalFunc: func(w http.ResponseWriter, r *http.Request)  {
@@ -49,16 +49,16 @@ var _ ServerInterface = &ServerInterfaceMock{}
 //             GetSimpleFunc: func(w http.ResponseWriter, r *http.Request)  {
 // 	               panic("mock out the GetSimple method")
 //             },
-//             GetWithArgsFunc: func(w http.ResponseWriter, r *http.Request)  {
+//             GetWithArgsFunc: func(w http.ResponseWriter, r *http.Request, params GetWithArgsParams)  {
 // 	               panic("mock out the GetWithArgs method")
 //             },
-//             GetWithContentTypeFunc: func(w http.ResponseWriter, r *http.Request)  {
+//             GetWithContentTypeFunc: func(w http.ResponseWriter, r *http.Request, contentType string)  {
 // 	               panic("mock out the GetWithContentType method")
 //             },
-//             GetWithReferencesFunc: func(w http.ResponseWriter, r *http.Request)  {
+//             GetWithReferencesFunc: func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument)  {
 // 	               panic("mock out the GetWithReferences method")
 //             },
-//             UpdateResource3Func: func(w http.ResponseWriter, r *http.Request)  {
+//             UpdateResource3Func: func(w http.ResponseWriter, r *http.Request, pFallthrough int)  {
 // 	               panic("mock out the UpdateResource3 method")
 //             },
 //         }
@@ -69,10 +69,10 @@ var _ ServerInterface = &ServerInterfaceMock{}
 //     }
 type ServerInterfaceMock struct {
 	// CreateResourceFunc mocks the CreateResource method.
-	CreateResourceFunc func(w http.ResponseWriter, r *http.Request)
+	CreateResourceFunc func(w http.ResponseWriter, r *http.Request, argument Argument)
 
 	// CreateResource2Func mocks the CreateResource2 method.
-	CreateResource2Func func(w http.ResponseWriter, r *http.Request)
+	CreateResource2Func func(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params)
 
 	// GetEveryTypeOptionalFunc mocks the GetEveryTypeOptional method.
 	GetEveryTypeOptionalFunc func(w http.ResponseWriter, r *http.Request)
@@ -87,16 +87,16 @@ type ServerInterfaceMock struct {
 	GetSimpleFunc func(w http.ResponseWriter, r *http.Request)
 
 	// GetWithArgsFunc mocks the GetWithArgs method.
-	GetWithArgsFunc func(w http.ResponseWriter, r *http.Request)
+	GetWithArgsFunc func(w http.ResponseWriter, r *http.Request, params GetWithArgsParams)
 
 	// GetWithContentTypeFunc mocks the GetWithContentType method.
-	GetWithContentTypeFunc func(w http.ResponseWriter, r *http.Request)
+	GetWithContentTypeFunc func(w http.ResponseWriter, r *http.Request, contentType string)
 
 	// GetWithReferencesFunc mocks the GetWithReferences method.
-	GetWithReferencesFunc func(w http.ResponseWriter, r *http.Request)
+	GetWithReferencesFunc func(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument)
 
 	// UpdateResource3Func mocks the UpdateResource3 method.
-	UpdateResource3Func func(w http.ResponseWriter, r *http.Request)
+	UpdateResource3Func func(w http.ResponseWriter, r *http.Request, pFallthrough int)
 
 	// calls tracks calls to the methods.
 	calls struct {
@@ -106,6 +106,8 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// Argument is the argument argument value.
+			Argument Argument
 		}
 		// CreateResource2 holds details about calls to the CreateResource2 method.
 		CreateResource2 []struct {
@@ -113,6 +115,10 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// InlineArgument is the inlineArgument argument value.
+			InlineArgument int
+			// Params is the params argument value.
+			Params CreateResource2Params
 		}
 		// GetEveryTypeOptional holds details about calls to the GetEveryTypeOptional method.
 		GetEveryTypeOptional []struct {
@@ -148,6 +154,8 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// Params is the params argument value.
+			Params GetWithArgsParams
 		}
 		// GetWithContentType holds details about calls to the GetWithContentType method.
 		GetWithContentType []struct {
@@ -155,6 +163,8 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// ContentType is the contentType argument value.
+			ContentType string
 		}
 		// GetWithReferences holds details about calls to the GetWithReferences method.
 		GetWithReferences []struct {
@@ -162,6 +172,10 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// GlobalArgument is the globalArgument argument value.
+			GlobalArgument int64
+			// Argument is the argument argument value.
+			Argument Argument
 		}
 		// UpdateResource3 holds details about calls to the UpdateResource3 method.
 		UpdateResource3 []struct {
@@ -169,38 +183,44 @@ type ServerInterfaceMock struct {
 			W http.ResponseWriter
 			// R is the r argument value.
 			R *http.Request
+			// PFallthrough is the pFallthrough argument value.
+			PFallthrough int
 		}
 	}
 }
 
 // CreateResource calls CreateResourceFunc.
-func (mock *ServerInterfaceMock) CreateResource(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) CreateResource(w http.ResponseWriter, r *http.Request, argument Argument) {
 	if mock.CreateResourceFunc == nil {
 		panic("ServerInterfaceMock.CreateResourceFunc: method is nil but ServerInterface.CreateResource was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W        http.ResponseWriter
+		R        *http.Request
+		Argument Argument
 	}{
-		W: w,
-		R: r,
+		W:        w,
+		R:        r,
+		Argument: argument,
 	}
 	lockServerInterfaceMockCreateResource.Lock()
 	mock.calls.CreateResource = append(mock.calls.CreateResource, callInfo)
 	lockServerInterfaceMockCreateResource.Unlock()
-	mock.CreateResourceFunc(w, r)
+	mock.CreateResourceFunc(w, r, argument)
 }
 
 // CreateResourceCalls gets all the calls that were made to CreateResource.
 // Check the length with:
 //     len(mockedServerInterface.CreateResourceCalls())
 func (mock *ServerInterfaceMock) CreateResourceCalls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W        http.ResponseWriter
+	R        *http.Request
+	Argument Argument
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W        http.ResponseWriter
+		R        *http.Request
+		Argument Argument
 	}
 	lockServerInterfaceMockCreateResource.RLock()
 	calls = mock.calls.CreateResource
@@ -209,33 +229,41 @@ func (mock *ServerInterfaceMock) CreateResourceCalls() []struct {
 }
 
 // CreateResource2 calls CreateResource2Func.
-func (mock *ServerInterfaceMock) CreateResource2(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) CreateResource2(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params) {
 	if mock.CreateResource2Func == nil {
 		panic("ServerInterfaceMock.CreateResource2Func: method is nil but ServerInterface.CreateResource2 was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W              http.ResponseWriter
+		R              *http.Request
+		InlineArgument int
+		Params         CreateResource2Params
 	}{
-		W: w,
-		R: r,
+		W:              w,
+		R:              r,
+		InlineArgument: inlineArgument,
+		Params:         params,
 	}
 	lockServerInterfaceMockCreateResource2.Lock()
 	mock.calls.CreateResource2 = append(mock.calls.CreateResource2, callInfo)
 	lockServerInterfaceMockCreateResource2.Unlock()
-	mock.CreateResource2Func(w, r)
+	mock.CreateResource2Func(w, r, inlineArgument, params)
 }
 
 // CreateResource2Calls gets all the calls that were made to CreateResource2.
 // Check the length with:
 //     len(mockedServerInterface.CreateResource2Calls())
 func (mock *ServerInterfaceMock) CreateResource2Calls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W              http.ResponseWriter
+	R              *http.Request
+	InlineArgument int
+	Params         CreateResource2Params
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W              http.ResponseWriter
+		R              *http.Request
+		InlineArgument int
+		Params         CreateResource2Params
 	}
 	lockServerInterfaceMockCreateResource2.RLock()
 	calls = mock.calls.CreateResource2
@@ -384,33 +412,37 @@ func (mock *ServerInterfaceMock) GetSimpleCalls() []struct {
 }
 
 // GetWithArgs calls GetWithArgsFunc.
-func (mock *ServerInterfaceMock) GetWithArgs(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) GetWithArgs(w http.ResponseWriter, r *http.Request, params GetWithArgsParams) {
 	if mock.GetWithArgsFunc == nil {
 		panic("ServerInterfaceMock.GetWithArgsFunc: method is nil but ServerInterface.GetWithArgs was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W      http.ResponseWriter
+		R      *http.Request
+		Params GetWithArgsParams
 	}{
-		W: w,
-		R: r,
+		W:      w,
+		R:      r,
+		Params: params,
 	}
 	lockServerInterfaceMockGetWithArgs.Lock()
 	mock.calls.GetWithArgs = append(mock.calls.GetWithArgs, callInfo)
 	lockServerInterfaceMockGetWithArgs.Unlock()
-	mock.GetWithArgsFunc(w, r)
+	mock.GetWithArgsFunc(w, r, params)
 }
 
 // GetWithArgsCalls gets all the calls that were made to GetWithArgs.
 // Check the length with:
 //     len(mockedServerInterface.GetWithArgsCalls())
 func (mock *ServerInterfaceMock) GetWithArgsCalls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W      http.ResponseWriter
+	R      *http.Request
+	Params GetWithArgsParams
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W      http.ResponseWriter
+		R      *http.Request
+		Params GetWithArgsParams
 	}
 	lockServerInterfaceMockGetWithArgs.RLock()
 	calls = mock.calls.GetWithArgs
@@ -419,33 +451,37 @@ func (mock *ServerInterfaceMock) GetWithArgsCalls() []struct {
 }
 
 // GetWithContentType calls GetWithContentTypeFunc.
-func (mock *ServerInterfaceMock) GetWithContentType(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) GetWithContentType(w http.ResponseWriter, r *http.Request, contentType string) {
 	if mock.GetWithContentTypeFunc == nil {
 		panic("ServerInterfaceMock.GetWithContentTypeFunc: method is nil but ServerInterface.GetWithContentType was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W           http.ResponseWriter
+		R           *http.Request
+		ContentType string
 	}{
-		W: w,
-		R: r,
+		W:           w,
+		R:           r,
+		ContentType: contentType,
 	}
 	lockServerInterfaceMockGetWithContentType.Lock()
 	mock.calls.GetWithContentType = append(mock.calls.GetWithContentType, callInfo)
 	lockServerInterfaceMockGetWithContentType.Unlock()
-	mock.GetWithContentTypeFunc(w, r)
+	mock.GetWithContentTypeFunc(w, r, contentType)
 }
 
 // GetWithContentTypeCalls gets all the calls that were made to GetWithContentType.
 // Check the length with:
 //     len(mockedServerInterface.GetWithContentTypeCalls())
 func (mock *ServerInterfaceMock) GetWithContentTypeCalls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W           http.ResponseWriter
+	R           *http.Request
+	ContentType string
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W           http.ResponseWriter
+		R           *http.Request
+		ContentType string
 	}
 	lockServerInterfaceMockGetWithContentType.RLock()
 	calls = mock.calls.GetWithContentType
@@ -454,33 +490,41 @@ func (mock *ServerInterfaceMock) GetWithContentTypeCalls() []struct {
 }
 
 // GetWithReferences calls GetWithReferencesFunc.
-func (mock *ServerInterfaceMock) GetWithReferences(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) GetWithReferences(w http.ResponseWriter, r *http.Request, globalArgument int64, argument Argument) {
 	if mock.GetWithReferencesFunc == nil {
 		panic("ServerInterfaceMock.GetWithReferencesFunc: method is nil but ServerInterface.GetWithReferences was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W              http.ResponseWriter
+		R              *http.Request
+		GlobalArgument int64
+		Argument       Argument
 	}{
-		W: w,
-		R: r,
+		W:              w,
+		R:              r,
+		GlobalArgument: globalArgument,
+		Argument:       argument,
 	}
 	lockServerInterfaceMockGetWithReferences.Lock()
 	mock.calls.GetWithReferences = append(mock.calls.GetWithReferences, callInfo)
 	lockServerInterfaceMockGetWithReferences.Unlock()
-	mock.GetWithReferencesFunc(w, r)
+	mock.GetWithReferencesFunc(w, r, globalArgument, argument)
 }
 
 // GetWithReferencesCalls gets all the calls that were made to GetWithReferences.
 // Check the length with:
 //     len(mockedServerInterface.GetWithReferencesCalls())
 func (mock *ServerInterfaceMock) GetWithReferencesCalls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W              http.ResponseWriter
+	R              *http.Request
+	GlobalArgument int64
+	Argument       Argument
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W              http.ResponseWriter
+		R              *http.Request
+		GlobalArgument int64
+		Argument       Argument
 	}
 	lockServerInterfaceMockGetWithReferences.RLock()
 	calls = mock.calls.GetWithReferences
@@ -489,33 +533,37 @@ func (mock *ServerInterfaceMock) GetWithReferencesCalls() []struct {
 }
 
 // UpdateResource3 calls UpdateResource3Func.
-func (mock *ServerInterfaceMock) UpdateResource3(w http.ResponseWriter, r *http.Request) {
+func (mock *ServerInterfaceMock) UpdateResource3(w http.ResponseWriter, r *http.Request, pFallthrough int) {
 	if mock.UpdateResource3Func == nil {
 		panic("ServerInterfaceMock.UpdateResource3Func: method is nil but ServerInterface.UpdateResource3 was just called")
 	}
 	callInfo := struct {
-		W http.ResponseWriter
-		R *http.Request
+		W            http.ResponseWriter
+		R            *http.Request
+		PFallthrough int
 	}{
-		W: w,
-		R: r,
+		W:            w,
+		R:            r,
+		PFallthrough: pFallthrough,
 	}
 	lockServerInterfaceMockUpdateResource3.Lock()
 	mock.calls.UpdateResource3 = append(mock.calls.UpdateResource3, callInfo)
 	lockServerInterfaceMockUpdateResource3.Unlock()
-	mock.UpdateResource3Func(w, r)
+	mock.UpdateResource3Func(w, r, pFallthrough)
 }
 
 // UpdateResource3Calls gets all the calls that were made to UpdateResource3.
 // Check the length with:
 //     len(mockedServerInterface.UpdateResource3Calls())
 func (mock *ServerInterfaceMock) UpdateResource3Calls() []struct {
-	W http.ResponseWriter
-	R *http.Request
+	W            http.ResponseWriter
+	R            *http.Request
+	PFallthrough int
 } {
 	var calls []struct {
-		W http.ResponseWriter
-		R *http.Request
+		W            http.ResponseWriter
+		R            *http.Request
+		PFallthrough int
 	}
 	lockServerInterfaceMockUpdateResource3.RLock()
 	calls = mock.calls.UpdateResource3
diff --git a/internal/test/server/server_test.go b/internal/test/server/server_test.go
index 31c40c4..de25598 100644
--- a/internal/test/server/server_test.go
+++ b/internal/test/server/server_test.go
@@ -11,12 +11,9 @@ import (
 func TestParameters(t *testing.T) {
 	m := ServerInterfaceMock{}
 
-	m.CreateResource2Func = func(w http.ResponseWriter, r *http.Request) {
-		params := ParamsForCreateResource2(r.Context())
-		arg := r.Context().Value("inlineArgument").(int)
-
+	m.CreateResource2Func = func(w http.ResponseWriter, r *http.Request, inlineArgument int, params CreateResource2Params) {
 		assert.Equal(t, 99, *params.InlineQueryArgument)
-		assert.Equal(t, 1, arg)
+		assert.Equal(t, 1, inlineArgument)
 	}
 
 	h := Handler(&m)
diff --git a/internal/test/test-schema.yaml b/internal/test/test-schema.yaml
index a2fbf31..cc34a9f 100644
--- a/internal/test/test-schema.yaml
+++ b/internal/test/test-schema.yaml
@@ -248,6 +248,9 @@ components:
           type: boolean
         string_field:
           type: string
+        email_field:
+          type: string
+          format: email
         date_field:
           type: string
           format: date
diff --git a/pkg/types/email_test.go b/pkg/types/email_test.go
new file mode 100644
index 0000000..69fb975
--- /dev/null
+++ b/pkg/types/email_test.go
@@ -0,0 +1,31 @@
+package types
+
+import (
+	"encoding/json"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestEmail_MarshalJSON(t *testing.T) {
+	testEmail := "gaben@valvesoftware.com"
+	b := struct {
+		EmailField Email `json:"email"`
+	}{
+		EmailField: Email(testEmail),
+	}
+	jsonBytes, err := json.Marshal(b)
+	assert.NoError(t, err)
+	assert.JSONEq(t, `{"email":"gaben@valvesoftware.com"}`, string(jsonBytes))
+}
+
+func TestEmail_UnmarshalJSON(t *testing.T) {
+	testEmail := Email("gaben@valvesoftware.com")
+	jsonStr := `{"email":"gaben@valvesoftware.com"}`
+	b := struct {
+		EmailField Email `json:"email"`
+	}{}
+	err := json.Unmarshal([]byte(jsonStr), &b)
+	assert.NoError(t, err)
+	assert.Equal(t, testEmail, b.EmailField)
+}
diff --git a/pkg/util/inputmapping_test.go b/pkg/util/inputmapping_test.go
new file mode 100644
index 0000000..4310463
--- /dev/null
+++ b/pkg/util/inputmapping_test.go
@@ -0,0 +1,62 @@
+package util
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestParseInputMapping(t *testing.T) {
+	var src string
+	var expected, parsed map[string]string
+	var err error
+
+	src = "key1:value1,key2:value2"
+	expected = map[string]string{"key1": "value1", "key2":"value2"}
+	parsed, err = ParseCommandlineMap(src)
+	require.NoError(t, err)
+	assert.Equal(t, expected, parsed)
+
+	src = `key1:"value1,value2",key2:value3`
+	expected = map[string]string{"key1": "value1,value2", "key2":"value3"}
+	parsed, err = ParseCommandlineMap(src)
+	require.NoError(t, err)
+	assert.Equal(t, expected, parsed)
+
+	src = `key1:"value1,value2,key2:value3"`
+	expected = map[string]string{"key1": "value1,value2,key2:value3"}
+	parsed, err = ParseCommandlineMap(src)
+	require.NoError(t, err)
+	assert.Equal(t, expected, parsed)
+
+	src = `"key1,key2":value1`
+	expected = map[string]string{"key1,key2": "value1"}
+	parsed, err = ParseCommandlineMap(src)
+	require.NoError(t, err)
+	assert.Equal(t, expected, parsed)
+}
+
+func TestSplitString(t *testing.T) {
+	var src string
+	var expected, result []string
+	var err error
+
+	src = "1,2,3"
+	expected = []string{"1", "2", "3"}
+	result = splitString(src, ',')
+	require.NoError(t, err)
+	assert.Equal(t, expected, result)
+
+	src = `"1,2",3`
+	expected = []string{`"1,2"`,"3"}
+	result = splitString(src, ',')
+	require.NoError(t, err)
+	assert.Equal(t, expected, result)
+
+	src = `1,"2,3",`
+	expected = []string{"1",`"2,3"`, ""}
+	result = splitString(src, ',')
+	require.NoError(t, err)
+	assert.Equal(t, expected, result)
+}