Repository: deepmap/oapi-codegen
Pull request #554 (2022-04-19 17:47:20)

Update gin example to use most recent release changes
The most recent release of `oapi-codegen` (v1.10.0) included changes
that broke the existing examples which were introduced in https://github.com/deepmap/oapi-codegen/pull/530

This change updates the `gin` example by regenerating the code-generated files and
updating the router registration to work with the new generated code.

It's worth considering whether we should update the `gin` template to return a concrete
type instead of the `IRouter` interface.

When the `IRouter` interface was introduced it seemed to exist just to allow mocking of the
interface (https://github.com/gin-gonic/gin/pull/1607) so I think it may be reasonable to just return 
a `gin.RouterGroup` from the `RegisterHandlersWithOptions` and `RegisterHandlers` generated functions.


Patch:
diff --git a/examples/petstore-expanded/gin/api/petstore-server.gen.go b/examples/petstore-expanded/gin/api/petstore-server.gen.go
index 9ed0d50..0cec0c1 100644
--- a/examples/petstore-expanded/gin/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/gin/api/petstore-server.gen.go
@@ -1,6 +1,6 @@
 // Package api provides primitives to interact with the openapi HTTP API.
 //
-// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+// Code generated by github.com/deepmap/oapi-codegen version v1.10.0 DO NOT EDIT.
 package api
 
 import (
@@ -36,12 +36,9 @@ type ServerInterface interface {
 
 // ServerInterfaceWrapper converts contexts to parameters.
 type ServerInterfaceWrapper struct {
-	Handler            ServerInterface
-	HandlerMiddlewares []MiddlewareFunc
+	Handler ServerInterface
 }
 
-type MiddlewareFunc func(c *gin.Context)
-
 // FindPets operation middleware
 func (siw *ServerInterfaceWrapper) FindPets(c *gin.Context) {
 
@@ -72,20 +69,12 @@ func (siw *ServerInterfaceWrapper) FindPets(c *gin.Context) {
 		return
 	}
 
-	for _, middleware := range siw.HandlerMiddlewares {
-		middleware(c)
-	}
-
 	siw.Handler.FindPets(c, params)
 }
 
 // AddPet operation middleware
 func (siw *ServerInterfaceWrapper) AddPet(c *gin.Context) {
 
-	for _, middleware := range siw.HandlerMiddlewares {
-		middleware(c)
-	}
-
 	siw.Handler.AddPet(c)
 }
 
@@ -103,10 +92,6 @@ func (siw *ServerInterfaceWrapper) DeletePet(c *gin.Context) {
 		return
 	}
 
-	for _, middleware := range siw.HandlerMiddlewares {
-		middleware(c)
-	}
-
 	siw.Handler.DeletePet(c, id)
 }
 
@@ -124,31 +109,31 @@ func (siw *ServerInterfaceWrapper) FindPetByID(c *gin.Context) {
 		return
 	}
 
-	for _, middleware := range siw.HandlerMiddlewares {
-		middleware(c)
-	}
-
 	siw.Handler.FindPetByID(c, id)
 }
 
 // GinServerOptions provides options for the Gin server.
 type GinServerOptions struct {
 	BaseURL     string
-	Middlewares []MiddlewareFunc
+	Middlewares []gin.HandlerFunc
 }
 
+// keeping for backward compatibility
+type MiddlewareFunc = gin.HandlerFunc
+
 // RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
-func RegisterHandlers(router *gin.Engine, si ServerInterface) *gin.Engine {
+func RegisterHandlers(router gin.IRouter, si ServerInterface) gin.IRouter {
 	return RegisterHandlersWithOptions(router, si, GinServerOptions{})
 }
 
 // RegisterHandlersWithOptions creates http.Handler with additional options
-func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options GinServerOptions) *gin.Engine {
+func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) gin.IRouter {
 	wrapper := ServerInterfaceWrapper{
-		Handler:            si,
-		HandlerMiddlewares: options.Middlewares,
+		Handler: si,
 	}
 
+	router = router.Group("/", options.Middlewares...)
+
 	router.GET(options.BaseURL+"/pets", wrapper.FindPets)
 
 	router.POST(options.BaseURL+"/pets", wrapper.AddPet)
@@ -158,7 +143,9 @@ func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options
 	router.GET(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
 
 	return router
-} // Base64 encoded, gzipped, json marshaled Swagger object
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
 	"H4sIAAAAAAAC/+RXW48budH9KwV+32OnNbEXedBTvB4vICBrT+LdvKznoYZdkmrBSw9Z1FgY6L8HRbZu",
diff --git a/examples/petstore-expanded/gin/api/petstore-types.gen.go b/examples/petstore-expanded/gin/api/petstore-types.gen.go
index 24add64..d56ecc7 100644
--- a/examples/petstore-expanded/gin/api/petstore-types.gen.go
+++ b/examples/petstore-expanded/gin/api/petstore-types.gen.go
@@ -1,6 +1,6 @@
 // Package api provides primitives to interact with the openapi HTTP API.
 //
-// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+// Code generated by github.com/deepmap/oapi-codegen version v1.10.0 DO NOT EDIT.
 package api
 
 // Error defines model for Error.
diff --git a/examples/petstore-expanded/gin/petstore.go b/examples/petstore-expanded/gin/petstore.go
index ea12fdf..ba8d968 100644
--- a/examples/petstore-expanded/gin/petstore.go
+++ b/examples/petstore-expanded/gin/petstore.go
@@ -36,7 +36,9 @@ func NewGinPetServer(petStore *api.PetStore, port int) *http.Server {
 	r.Use(middleware.OapiRequestValidator(swagger))
 
 	// We now register our petStore above as the handler for the interface
-	r = api.RegisterHandlers(r, petStore)
+	apiRoutes := api.RegisterHandlers(r, petStore).(*gin.RouterGroup)
+
+	r.Use(apiRoutes.Handlers...)
 
 	s := &http.Server{
 		Handler: r,
diff --git a/pkg/codegen/templates/gin/gin-register.tmpl b/pkg/codegen/templates/gin/gin-register.tmpl
index cdd11e8..5362dd7 100644
--- a/pkg/codegen/templates/gin/gin-register.tmpl
+++ b/pkg/codegen/templates/gin/gin-register.tmpl
@@ -1,26 +1,21 @@
 // GinServerOptions provides options for the Gin server.
 type GinServerOptions struct {
     BaseURL string
-    Middlewares []gin.HandlerFunc
+    Middlewares []MiddlewareFunc
 }
 
-// keeping for backward compatibility
-type MiddlewareFunc = gin.HandlerFunc
-
 // RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
-func RegisterHandlers(router gin.IRouter, si ServerInterface) gin.IRouter {
+func RegisterHandlers(router *gin.Engine, si ServerInterface) *gin.Engine {
   return RegisterHandlersWithOptions(router, si, GinServerOptions{})
 }
 
 // RegisterHandlersWithOptions creates http.Handler with additional options
-func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) gin.IRouter {
+func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options GinServerOptions) *gin.Engine {
 {{if .}}wrapper := ServerInterfaceWrapper{
 Handler: si,
+HandlerMiddlewares: options.Middlewares,
 }
 {{end}}
-
-router = router.Group("/", options.Middlewares...)
-
 {{range .}}
 router.{{.Method }}(options.BaseURL+"{{.Path | swaggerUriToGinUri }}", wrapper.{{.OperationId}})
 {{end}}
diff --git a/pkg/codegen/templates/gin/gin-wrappers.tmpl b/pkg/codegen/templates/gin/gin-wrappers.tmpl
index 16df520..27bf19c 100644
--- a/pkg/codegen/templates/gin/gin-wrappers.tmpl
+++ b/pkg/codegen/templates/gin/gin-wrappers.tmpl
@@ -1,8 +1,11 @@
 // ServerInterfaceWrapper converts contexts to parameters.
 type ServerInterfaceWrapper struct {
     Handler ServerInterface
+    HandlerMiddlewares []MiddlewareFunc
 }
 
+type MiddlewareFunc func(c *gin.Context)
+
 {{range .}}{{$opid := .OperationId}}
 
 // {{$opid}} operation middleware
@@ -162,6 +165,10 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) {
     {{end}}
   {{end}}
 
+  for _, middleware := range siw.HandlerMiddlewares {
+    middleware(c)
+  }
+
   siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
 }
 {{end}}
diff --git a/pkg/gin-middleware/oapi_validate.go b/pkg/gin-middleware/oapi_validate.go
index 44075c3..82a885e 100644
--- a/pkg/gin-middleware/oapi_validate.go
+++ b/pkg/gin-middleware/oapi_validate.go
@@ -72,8 +72,6 @@ func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) gin.
 		err := ValidateRequestFromContext(c, router, options)
 		if err != nil {
 			// note: i am not sure if this is the best way to handle this
-			// todo: the error message should be customizable by the user
-			c.Error(err)
 			c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()})
 		}
 		c.Next()
@@ -126,9 +124,6 @@ func ValidateRequestFromContext(c *gin.Context, router routers.Router, options *
 			errorLines := strings.Split(e.Error(), "\n")
 			return fmt.Errorf("error in openapi3filter.RequestError: %s", errorLines[0])
 		case *openapi3filter.SecurityRequirementsError:
-			for _, e := range e.Errors {
-				c.Error(e)
-			}
 			return fmt.Errorf("error in openapi3filter.SecurityRequirementsError: %s", e.Error())
 		default:
 			// This should never happen today, but if our upstream code changes,
diff --git a/pkg/types/uuid.go b/pkg/types/uuid.go
index a4fb36d..50515d8 100644
--- a/pkg/types/uuid.go
+++ b/pkg/types/uuid.go
@@ -7,13 +7,10 @@ import (
 	"github.com/google/uuid"
 )
 
-type UUID string
+type UUID uuid.UUID
 
 func (u UUID) MarshalJSON() ([]byte, error) {
-	if _, err := uuid.Parse(string(u)); err != nil {
-		return nil, errors.New("uuid: failed to pass validation")
-	}
-	return json.Marshal(string(u))
+	return json.Marshal(uuid.UUID(u).String())
 }
 
 func (u *UUID) UnmarshalJSON(data []byte) error {
@@ -21,9 +18,10 @@ func (u *UUID) UnmarshalJSON(data []byte) error {
 	if err := json.Unmarshal(data, &s); err != nil {
 		return err
 	}
-	if _, err := uuid.Parse(s); err != nil {
+	parsed, err := uuid.Parse(s)
+	if err != nil {
 		return errors.New("uuid: failed to pass validation")
 	}
-	*u = UUID(s)
+	*u = UUID(parsed)
 	return nil
 }


Test patch:
diff --git a/pkg/types/uuid_test.go b/pkg/types/uuid_test.go
index 7f19aaa..4ac689a 100644
--- a/pkg/types/uuid_test.go
+++ b/pkg/types/uuid_test.go
@@ -4,22 +4,24 @@ import (
 	"encoding/json"
 	"testing"
 
+	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 )
 
-func TestUUID_MarshalJSON_Fail(t *testing.T) {
-	testUUID := "this-is-not-a-uuid"
+func TestUUID_MarshalJSON_Zero(t *testing.T) {
+	var testUUID UUID
 	b := struct {
 		UUIDField UUID `json:"uuid"`
 	}{
-		UUIDField: UUID(testUUID),
+		UUIDField: testUUID,
 	}
-	_, err := json.Marshal(b)
-	assert.Error(t, err)
+	marshaled, err := json.Marshal(b)
+	assert.NoError(t, err)
+	assert.JSONEq(t, `{"uuid":"00000000-0000-0000-0000-000000000000"}`, string(marshaled))
 }
 
 func TestUUID_MarshalJSON_Pass(t *testing.T) {
-	testUUID := "9cb14230-b640-11ec-b909-0242ac120002"
+	testUUID := uuid.MustParse("9cb14230-b640-11ec-b909-0242ac120002")
 	b := struct {
 		UUIDField UUID `json:"uuid"`
 	}{
@@ -40,7 +42,7 @@ func TestUUID_UnmarshalJSON_Fail(t *testing.T) {
 }
 
 func TestUUID_UnmarshalJSON_Pass(t *testing.T) {
-	testUUID := UUID("9cb14230-b640-11ec-b909-0242ac120002")
+	testUUID := UUID(uuid.MustParse("9cb14230-b640-11ec-b909-0242ac120002"))
 	jsonStr := `{"uuid":"9cb14230-b640-11ec-b909-0242ac120002"}`
 	b := struct {
 		UUIDField UUID `json:"uuid"`