Repository: deepmap/oapi-codegen
Pull request #136 (2020-02-18 15:50:28)

Add support for user-provided templates
This change introduces a new `-templates` command flag to enable users
to override code generation templates at runtime instead of having to
recompile the binary after having edited the built-in templates.

falzm: @deepmap-marcinr let me know if this is an acceptable change to be merged upstream: we'd like to use your software at [Exoscale](https://www.exoscale.com/) and we'd greatly appreciate not having to fork it 😉 

Patch:
diff --git a/README.md b/README.md
index a260248..cb0ed44 100644
--- a/README.md
+++ b/README.md
@@ -454,4 +454,16 @@ Go file.
 Afterwards you should run `go generate ./...`, and the templates will be updated
  accordingly.
 
-
+Alternatively, you can provide custom templates to override built-in ones using
+the `-templates` flag specifying a path to a directory containing templates
+files. These files **must** be named identically to built-in template files
+(see `pkg/codegen/templates/*.tmpl` in the source code), and will be interpreted
+on-the-fly at run time. Example:
+
+    $ ls -1 my-templates/
+    client.tmpl
+    typedef.tmpl
+    $ oapi-codegen \
+        -templates my-templates/ \
+        -generate types,client \
+        petstore-expanded.yaml
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index 1a1b080..164c49a 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -18,6 +18,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
+	"path"
 	"path/filepath"
 	"strings"
 
@@ -32,11 +33,12 @@ func errExit(format string, args ...interface{}) {
 
 func main() {
 	var (
-		packageName string
-		generate    string
-		outputFile  string
-		includeTags string
-		excludeTags string
+		packageName  string
+		generate     string
+		outputFile   string
+		includeTags  string
+		excludeTags  string
+		templatesDir string
 	)
 	flag.StringVar(&packageName, "package", "", "The package name for generated code")
 	flag.StringVar(&generate, "generate", "types,client,server,spec",
@@ -44,6 +46,7 @@ func main() {
 	flag.StringVar(&outputFile, "o", "", "Where to output generated code, stdout is default")
 	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.Parse()
 
 	if flag.NArg() < 1 {
@@ -95,6 +98,12 @@ func main() {
 		errExit("error loading swagger spec\n: %s", err)
 	}
 
+	templates, err := loadTemplateOverrides(templatesDir)
+	if err != nil {
+		errExit("error loading template overrides: %s\n", err)
+	}
+	opts.UserTemplates = templates
+
 	code, err := codegen.Generate(swagger, packageName, opts)
 	if err != nil {
 		errExit("error generating code: %s\n", err)
@@ -125,3 +134,26 @@ func splitCSVArg(input string) []string {
 	}
 	return args
 }
+
+func loadTemplateOverrides(templatesDir string) (map[string]string, error) {
+	var templates = make(map[string]string)
+
+	if templatesDir == "" {
+		return templates, nil
+	}
+
+	files, err := ioutil.ReadDir(templatesDir)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, f := range files {
+		data, err := ioutil.ReadFile(path.Join(templatesDir, f.Name()))
+		if err != nil {
+			return nil, err
+		}
+		templates[f.Name()] = string(data)
+	}
+
+	return templates, nil
+}
diff --git a/examples/petstore-expanded/petstore-client.gen.go b/examples/petstore-expanded/petstore-client.gen.go
index ea6050c..095b9e9 100644
--- a/examples/petstore-expanded/petstore-client.gen.go
+++ b/examples/petstore-expanded/petstore-client.gen.go
@@ -227,7 +227,13 @@ func NewFindPetsRequest(server string, params *FindPetsParams) (*http.Request, e
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/pets"))
+
+	basePath := fmt.Sprintf("/pets")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -295,7 +301,13 @@ func NewAddPetRequestWithBody(server string, contentType string, body io.Reader)
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/pets"))
+
+	basePath := fmt.Sprintf("/pets")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -324,7 +336,13 @@ func NewDeletePetRequest(server string, id int64) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/pets/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/pets/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -352,7 +370,13 @@ func NewFindPetByIdRequest(server string, id int64) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/pets/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/pets/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 90015ee..10ca40f 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -32,14 +32,15 @@ import (
 
 // Options defines the optional code to generate.
 type Options struct {
-	GenerateChiServer  bool     // GenerateChiServer specifies whether to generate chi server boilerplate
-	GenerateEchoServer bool     // GenerateEchoServer specifies whether to generate echo server boilerplate
-	GenerateClient     bool     // GenerateClient specifies whether to generate client boilerplate
-	GenerateTypes      bool     // GenerateTypes specifies whether to generate type definitions
-	EmbedSpec          bool     // Whether to embed the swagger spec in the generated code
-	SkipFmt            bool     // Whether to skip go fmt on the generated code
-	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.
+	GenerateChiServer  bool              // GenerateChiServer specifies whether to generate chi server boilerplate
+	GenerateEchoServer bool              // GenerateEchoServer specifies whether to generate echo server boilerplate
+	GenerateClient     bool              // GenerateClient specifies whether to generate client boilerplate
+	GenerateTypes      bool              // GenerateTypes specifies whether to generate type definitions
+	EmbedSpec          bool              // Whether to embed the swagger spec in the generated code
+	SkipFmt            bool              // Whether to skip go fmt on the generated code
+	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
 }
 
 type goImport struct {
@@ -99,6 +100,16 @@ func Generate(swagger *openapi3.Swagger, packageName string, opts Options) (stri
 		return "", errors.Wrap(err, "error parsing oapi-codegen templates")
 	}
 
+	// Override built-in templates with user-provided versions
+	for _, tpl := range t.Templates() {
+		if _, ok := opts.UserTemplates[tpl.Name()]; ok {
+			utpl := t.New(tpl.Name())
+			if _, err := utpl.Parse(opts.UserTemplates[tpl.Name()]); err != nil {
+				return "", errors.Wrapf(err, "error parsing user-provided template %q", tpl.Name())
+			}
+		}
+	}
+
 	ops, err := OperationDefinitions(swagger)
 	if err != nil {
 		return "", errors.Wrap(err, "error creating operation definitions")
diff --git a/pkg/codegen/templates/client.tmpl b/pkg/codegen/templates/client.tmpl
index 19e0734..4ccfc43 100644
--- a/pkg/codegen/templates/client.tmpl
+++ b/pkg/codegen/templates/client.tmpl
@@ -164,7 +164,13 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr
     if err != nil {
         return nil, err
     }
-    queryUrl, err = queryUrl.Parse(fmt.Sprintf("{{genParamFmtString .Path}}"{{range $paramIdx, $param := .PathParams}}, pathParam{{$paramIdx}}{{end}}))
+
+    basePath := fmt.Sprintf("{{genParamFmtString .Path}}"{{range $paramIdx, $param := .PathParams}}, pathParam{{$paramIdx}}{{end}})
+    if basePath[0] == '/' {
+        basePath = basePath[1:]
+    }
+
+    queryUrl, err = queryUrl.Parse(basePath)
     if err != nil {
         return nil, err
     }
diff --git a/pkg/codegen/templates/templates.gen.go b/pkg/codegen/templates/templates.gen.go
index d674c6d..46fac72 100644
--- a/pkg/codegen/templates/templates.gen.go
+++ b/pkg/codegen/templates/templates.gen.go
@@ -541,7 +541,13 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr
     if err != nil {
         return nil, err
     }
-    queryUrl, err = queryUrl.Parse(fmt.Sprintf("{{genParamFmtString .Path}}"{{range $paramIdx, $param := .PathParams}}, pathParam{{$paramIdx}}{{end}}))
+
+    basePath := fmt.Sprintf("{{genParamFmtString .Path}}"{{range $paramIdx, $param := .PathParams}}, pathParam{{$paramIdx}}{{end}})
+    if basePath[0] == '/' {
+        basePath = basePath[1:]
+    }
+
+    queryUrl, err = queryUrl.Parse(basePath)
     if err != nil {
         return nil, err
     }
diff --git a/pkg/runtime/bindparam.go b/pkg/runtime/bindparam.go
index 18a4130..6378572 100644
--- a/pkg/runtime/bindparam.go
+++ b/pkg/runtime/bindparam.go
@@ -20,6 +20,7 @@ import (
 	"net/url"
 	"reflect"
 	"strings"
+	"time"
 
 	"github.com/deepmap/oapi-codegen/pkg/types"
 	"github.com/labstack/echo/v4"
@@ -436,6 +437,9 @@ func bindParamsToExplodedObject(paramName string, values url.Values, dest interf
 	switch dest.(type) {
 	case *types.Date:
 		return BindStringToObject(values.Get(paramName), dest)
+	case *time.Time:
+		return BindStringToObject(values.Get(paramName), dest)
+
 	}
 
 	v := reflect.Indirect(reflect.ValueOf(dest))
diff --git a/pkg/runtime/bindstring.go b/pkg/runtime/bindstring.go
index f3397a2..50df465 100644
--- a/pkg/runtime/bindstring.go
+++ b/pkg/runtime/bindstring.go
@@ -75,7 +75,10 @@ func BindStringToObject(src string, dst interface{}) error {
 			// Time is a special case of a struct that we handle
 			parsedTime, err := time.Parse(time.RFC3339Nano, src)
 			if err != nil {
-				return fmt.Errorf("error parsing '%s' as RFC3339 time: %s", src, err)
+				parsedTime, err = time.Parse(types.DateFormat, src)
+				if err != nil {
+					return fmt.Errorf("error parsing '%s' as RFC3339 or 2006-01-02 time: %s", src, err)
+				}
 			}
 			*dstType = parsedTime
 			return nil


Test patch:
diff --git a/internal/test/client/client.gen.go b/internal/test/client/client.gen.go
index 5965965..e18e1c8 100644
--- a/internal/test/client/client.gen.go
+++ b/internal/test/client/client.gen.go
@@ -284,7 +284,13 @@ func NewPostBothRequestWithBody(server string, contentType string, body io.Reade
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_both_bodies"))
+
+	basePath := fmt.Sprintf("/with_both_bodies")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -306,7 +312,13 @@ func NewGetBothRequest(server string) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_both_responses"))
+
+	basePath := fmt.Sprintf("/with_both_responses")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -338,7 +350,13 @@ func NewPostJsonRequestWithBody(server string, contentType string, body io.Reade
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_json_body"))
+
+	basePath := fmt.Sprintf("/with_json_body")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -360,7 +378,13 @@ func NewGetJsonRequest(server string) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_json_response"))
+
+	basePath := fmt.Sprintf("/with_json_response")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -381,7 +405,13 @@ func NewPostOtherRequestWithBody(server string, contentType string, body io.Read
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_other_body"))
+
+	basePath := fmt.Sprintf("/with_other_body")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -403,7 +433,13 @@ func NewGetOtherRequest(server string) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_other_response"))
+
+	basePath := fmt.Sprintf("/with_other_response")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -424,7 +460,13 @@ func NewGetJsonWithTrailingSlashRequest(server string) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/with_trailing_slash/"))
+
+	basePath := fmt.Sprintf("/with_trailing_slash/")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index a49fad1..fcd9997 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -848,7 +848,13 @@ func NewParamsWithAddPropsRequest(server string, params *ParamsWithAddPropsParam
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/params_with_add_props"))
+
+	basePath := fmt.Sprintf("/params_with_add_props")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -908,7 +914,13 @@ func NewBodyWithAddPropsRequestWithBody(server string, contentType string, body
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/params_with_add_props"))
+
+	basePath := fmt.Sprintf("/params_with_add_props")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/test/issues/issue-52/issue.gen.go b/internal/test/issues/issue-52/issue.gen.go
index f7a0d68..201152f 100644
--- a/internal/test/issues/issue-52/issue.gen.go
+++ b/internal/test/issues/issue-52/issue.gen.go
@@ -185,7 +185,13 @@ func NewExampleGetRequest(server string) (*http.Request, error) {
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/example"))
+
+	basePath := fmt.Sprintf("/example")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go
index 835b987..9a359d1 100644
--- a/internal/test/parameters/parameters.gen.go
+++ b/internal/test/parameters/parameters.gen.go
@@ -515,7 +515,13 @@ func NewGetContentObjectRequest(server string, param ComplexObject) (*http.Reque
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/contentObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/contentObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -536,7 +542,13 @@ func NewGetCookieRequest(server string, params *GetCookieParams) (*http.Request,
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/cookie"))
+
+	basePath := fmt.Sprintf("/cookie")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -664,7 +676,13 @@ func NewGetHeaderRequest(server string, params *GetHeaderParams) (*http.Request,
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/header"))
+
+	basePath := fmt.Sprintf("/header")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -771,7 +789,13 @@ func NewGetLabelExplodeArrayRequest(server string, param []int32) (*http.Request
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/labelExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/labelExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -799,7 +823,13 @@ func NewGetLabelExplodeObjectRequest(server string, param Object) (*http.Request
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/labelExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/labelExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -827,7 +857,13 @@ func NewGetLabelNoExplodeArrayRequest(server string, param []int32) (*http.Reque
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/labelNoExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/labelNoExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -855,7 +891,13 @@ func NewGetLabelNoExplodeObjectRequest(server string, param Object) (*http.Reque
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/labelNoExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/labelNoExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -883,7 +925,13 @@ func NewGetMatrixExplodeArrayRequest(server string, id []int32) (*http.Request,
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/matrixExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/matrixExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -911,7 +959,13 @@ func NewGetMatrixExplodeObjectRequest(server string, id Object) (*http.Request,
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/matrixExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/matrixExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -939,7 +993,13 @@ func NewGetMatrixNoExplodeArrayRequest(server string, id []int32) (*http.Request
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/matrixNoExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/matrixNoExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -967,7 +1027,13 @@ func NewGetMatrixNoExplodeObjectRequest(server string, id Object) (*http.Request
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/matrixNoExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/matrixNoExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -992,7 +1058,13 @@ func NewGetPassThroughRequest(server string, param string) (*http.Request, error
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/passThrough/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/passThrough/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1013,7 +1085,13 @@ func NewGetQueryFormRequest(server string, params *GetQueryFormParams) (*http.Re
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/queryForm"))
+
+	basePath := fmt.Sprintf("/queryForm")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1151,7 +1229,13 @@ func NewGetSimpleExplodeArrayRequest(server string, param []int32) (*http.Reques
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/simpleExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/simpleExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1179,7 +1263,13 @@ func NewGetSimpleExplodeObjectRequest(server string, param Object) (*http.Reques
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/simpleExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/simpleExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1207,7 +1297,13 @@ func NewGetSimpleNoExplodeArrayRequest(server string, param []int32) (*http.Requ
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/simpleNoExplodeArray/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/simpleNoExplodeArray/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1235,7 +1331,13 @@ func NewGetSimpleNoExplodeObjectRequest(server string, param Object) (*http.Requ
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/simpleNoExplodeObject/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/simpleNoExplodeObject/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -1263,7 +1365,13 @@ func NewGetSimplePrimitiveRequest(server string, param int32) (*http.Request, er
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/simplePrimitive/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/simplePrimitive/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go
index b8fe16f..c34e025 100644
--- a/internal/test/schemas/schemas.gen.go
+++ b/internal/test/schemas/schemas.gen.go
@@ -200,7 +200,13 @@ func NewIssue30Request(server string, pFallthrough string) (*http.Request, error
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/issues/30/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/issues/30/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -228,7 +234,13 @@ func NewIssue41Request(server string, n1param N5StartsWithNumber) (*http.Request
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/issues/41/%s", pathParam0))
+
+	basePath := fmt.Sprintf("/issues/41/%s", pathParam0)
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
@@ -260,7 +272,13 @@ func NewIssue9RequestWithBody(server string, params *Issue9Params, contentType s
 	if err != nil {
 		return nil, err
 	}
-	queryUrl, err = queryUrl.Parse(fmt.Sprintf("/issues/9"))
+
+	basePath := fmt.Sprintf("/issues/9")
+	if basePath[0] == '/' {
+		basePath = basePath[1:]
+	}
+
+	queryUrl, err = queryUrl.Parse(basePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go
index f534d9e..caa97da 100644
--- a/pkg/codegen/codegen_test.go
+++ b/pkg/codegen/codegen_test.go
@@ -59,6 +59,37 @@ func TestExamplePetStoreCodeGeneration(t *testing.T) {
 	assert.Len(t, problems, 0)
 }
 
+func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
+
+	userTemplates := map[string]string{"typedef.tmpl": "//blah"}
+
+	// Input vars for code generation:
+	packageName := "api"
+	opts := Options{
+		GenerateTypes: true,
+		UserTemplates: userTemplates,
+	}
+
+	// Get a spec from the example PetStore definition:
+	swagger, err := examplePetstore.GetSwagger()
+	assert.NoError(t, err)
+
+	// Run our code generation:
+	code, err := Generate(swagger, packageName, opts)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, code)
+
+	// Check that we have valid (formattable) code:
+	_, err = format.Source([]byte(code))
+	assert.NoError(t, err)
+
+	// Check that we have a package:
+	assert.Contains(t, code, "package api")
+
+	// Check that the built-in template has been overriden
+	assert.Contains(t, code, "//blah")
+}
+
 func TestExamplePetStoreParseFunction(t *testing.T) {
 
 	bodyBytes := []byte(`{"id": 5, "name": "testpet", "tag": "cat"}`)