From 93fa27b7cc40bd12aa12fadff0bbc51877cc7406 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 6 Nov 2023 16:10:37 +0000 Subject: [PATCH 01/25] Onboard to Release Drafter To give us a more consistent set of release notes, especially now we're using multi-repo setup, we can use it to build our upcoming release's changelog with a centrally configured + managed configuration. --- .github/release-drafter.yml | 1 + .github/workflows/release-drafter.yml | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/release-drafter.yml diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 000000000..852c651a5 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1 @@ +_extends: oapi-codegen/.github diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 000000000..e10c997ee --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,25 @@ +name: Release Drafter + +on: + push: + branches: + - master + workflow_dispatch: {} + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + with: + name: next + tag: next + version: next + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From a64d36daa38bf5b6586a1739e2d428a89881267b Mon Sep 17 00:00:00 2001 From: Nikita Boldasov Date: Wed, 15 Nov 2023 17:52:39 +0300 Subject: [PATCH 02/25] Ensure that `BindStyledParameter` handles required parameters correctly (#1315) Corresponding with changes in the runtime package, we want to ensure we let the (newly added) `BindStyledParameterWithOptions` function knows whether a parameter is required, to not error when an empty value is provided. Closes #954. --- examples/go.mod | 12 ++--- examples/go.sum | 24 ++++----- .../petstore-expanded/chi/api/petstore.gen.go | 4 +- .../echo/api/petstore-server.gen.go | 4 +- .../fiber/api/petstore-server.gen.go | 4 +- .../gin/api/petstore-server.gen.go | 4 +- .../gorilla/api/petstore.gen.go | 4 +- .../iris/api/petstore-server.gen.go | 4 +- .../strict/api/petstore-server.gen.go | 4 +- internal/test/go.mod | 12 ++--- internal/test/go.sum | 24 ++++----- internal/test/issues/issue-1180/issue.gen.go | 2 +- internal/test/issues/issue-312/issue.gen.go | 2 +- .../issue-grab_import_names/issue.gen.go | 4 +- internal/test/parameters/parameters.gen.go | 54 +++++++++---------- internal/test/schemas/schemas.gen.go | 6 +-- internal/test/server/server.gen.go | 14 ++--- internal/test/strict-server/chi/server.gen.go | 6 +-- .../test/strict-server/echo/server.gen.go | 6 +-- .../test/strict-server/fiber/server.gen.go | 6 +-- internal/test/strict-server/gin/server.gen.go | 6 +-- .../test/strict-server/gorilla/server.gen.go | 6 +-- .../test/strict-server/iris/server.gen.go | 6 +-- pkg/codegen/templates/chi/chi-middleware.tmpl | 6 +-- pkg/codegen/templates/echo/echo-wrappers.tmpl | 6 +-- .../templates/fiber/fiber-middleware.tmpl | 6 +-- pkg/codegen/templates/gin/gin-wrappers.tmpl | 6 +-- .../templates/gorilla/gorilla-middleware.tmpl | 6 +-- .../templates/iris/iris-middleware.tmpl | 6 +-- 29 files changed, 127 insertions(+), 127 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index 59c73704e..68852eafa 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -12,14 +12,14 @@ require ( github.com/gofiber/fiber/v2 v2.49.1 github.com/gorilla/mux v1.8.0 github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 - github.com/labstack/echo/v4 v4.11.1 + github.com/labstack/echo/v4 v4.11.3 github.com/lestrrat-go/jwx v1.2.26 github.com/oapi-codegen/echo-middleware v1.0.1 github.com/oapi-codegen/fiber-middleware v1.0.1 github.com/oapi-codegen/gin-middleware v1.0.1 github.com/oapi-codegen/iris-middleware v1.0.4 github.com/oapi-codegen/nethttp-middleware v1.0.1 - github.com/oapi-codegen/runtime v1.0.0 + github.com/oapi-codegen/runtime v1.1.0 github.com/oapi-codegen/testutil v1.0.0 github.com/stretchr/testify v1.8.4 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 @@ -52,7 +52,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/iris-contrib/schema v0.0.6 // indirect @@ -102,10 +102,10 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yosssi/ace v0.0.5 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.13.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.12.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index 22f79ec67..745725b5f 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -81,8 +81,8 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -123,8 +123,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= -github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= +github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM= +github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= @@ -178,8 +178,8 @@ github.com/oapi-codegen/iris-middleware v1.0.4 h1:+Eoqqbc90qCeBz3Zlzf1xb5xn+wfwQ github.com/oapi-codegen/iris-middleware v1.0.4/go.mod h1:D1IAqYMgm5QKbD9R+y0j8m+xUtiPLMG92iY+oos/NXc= github.com/oapi-codegen/nethttp-middleware v1.0.1 h1:ZWvwfnMU0eloHX1VEJmQscQm3741t0vCm0eSIie1NIo= github.com/oapi-codegen/nethttp-middleware v1.0.1/go.mod h1:P7xtAvpoqNB+5obR9qRCeefH7YlXWSK3KgPs/9WB8tE= -github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= -github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= +github.com/oapi-codegen/runtime v1.1.0 h1:rJpoNUawn5XTvekgfkvSZr0RqEnoYpFkyvrzfWeFKWM= +github.com/oapi-codegen/runtime v1.1.0/go.mod h1:BeSfBkWWWnAnGdyS+S/GnlbmHKzf8/hwkvelJZDeKA8= github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8= github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= @@ -260,8 +260,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -278,8 +278,8 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -301,8 +301,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/examples/petstore-expanded/chi/api/petstore.gen.go b/examples/petstore-expanded/chi/api/petstore.gen.go index 987312364..5f69e4bfa 100644 --- a/examples/petstore-expanded/chi/api/petstore.gen.go +++ b/examples/petstore-expanded/chi/api/petstore.gen.go @@ -173,7 +173,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Requ // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return @@ -199,7 +199,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Re // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return diff --git a/examples/petstore-expanded/echo/api/petstore-server.gen.go b/examples/petstore-expanded/echo/api/petstore-server.gen.go index ddc3a9eb9..e92c4a0cc 100644 --- a/examples/petstore-expanded/echo/api/petstore-server.gen.go +++ b/examples/petstore-expanded/echo/api/petstore-server.gen.go @@ -80,7 +80,7 @@ func (w *ServerInterfaceWrapper) DeletePet(ctx echo.Context) error { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } @@ -96,7 +96,7 @@ func (w *ServerInterfaceWrapper) FindPetByID(ctx echo.Context) error { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } diff --git a/examples/petstore-expanded/fiber/api/petstore-server.gen.go b/examples/petstore-expanded/fiber/api/petstore-server.gen.go index ffae3e81c..bb8538efd 100644 --- a/examples/petstore-expanded/fiber/api/petstore-server.gen.go +++ b/examples/petstore-expanded/fiber/api/petstore-server.gen.go @@ -85,7 +85,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(c *fiber.Ctx) error { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", c.Params("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", c.Params("id"), &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter id: %w", err).Error()) } @@ -101,7 +101,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(c *fiber.Ctx) error { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", c.Params("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", c.Params("id"), &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter id: %w", err).Error()) } diff --git a/examples/petstore-expanded/gin/api/petstore-server.gen.go b/examples/petstore-expanded/gin/api/petstore-server.gen.go index 06098b20a..f048e0066 100644 --- a/examples/petstore-expanded/gin/api/petstore-server.gen.go +++ b/examples/petstore-expanded/gin/api/petstore-server.gen.go @@ -98,7 +98,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(c *gin.Context) { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", c.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", c.Param("id"), &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter id: %w", err), http.StatusBadRequest) return @@ -122,7 +122,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(c *gin.Context) { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", c.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", c.Param("id"), &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter id: %w", err), http.StatusBadRequest) return diff --git a/examples/petstore-expanded/gorilla/api/petstore.gen.go b/examples/petstore-expanded/gorilla/api/petstore.gen.go index f5eb34862..2f65ea25b 100644 --- a/examples/petstore-expanded/gorilla/api/petstore.gen.go +++ b/examples/petstore-expanded/gorilla/api/petstore.gen.go @@ -145,7 +145,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Requ // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", mux.Vars(r)["id"], &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return @@ -171,7 +171,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Re // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameter("simple", false, "id", mux.Vars(r)["id"], &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", mux.Vars(r)["id"], &id, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return diff --git a/examples/petstore-expanded/iris/api/petstore-server.gen.go b/examples/petstore-expanded/iris/api/petstore-server.gen.go index 2ff76188e..3abf1dac4 100644 --- a/examples/petstore-expanded/iris/api/petstore-server.gen.go +++ b/examples/petstore-expanded/iris/api/petstore-server.gen.go @@ -85,7 +85,7 @@ func (w *ServerInterfaceWrapper) DeletePet(ctx iris.Context) { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Params().Get("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Params().Get("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter id: %s", err) @@ -104,7 +104,7 @@ func (w *ServerInterfaceWrapper) FindPetByID(ctx iris.Context) { // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, ctx.Params().Get("id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", ctx.Params().Get("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter id: %s", err) diff --git a/examples/petstore-expanded/strict/api/petstore-server.gen.go b/examples/petstore-expanded/strict/api/petstore-server.gen.go index 5a2180a86..dce258be7 100644 --- a/examples/petstore-expanded/strict/api/petstore-server.gen.go +++ b/examples/petstore-expanded/strict/api/petstore-server.gen.go @@ -134,7 +134,7 @@ func (siw *ServerInterfaceWrapper) DeletePet(w http.ResponseWriter, r *http.Requ // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return @@ -160,7 +160,7 @@ func (siw *ServerInterfaceWrapper) FindPetByID(w http.ResponseWriter, r *http.Re // ------------- Path parameter "id" ------------- var id int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "id", runtime.ParamLocationPath, chi.URLParam(r, "id"), &id) + err = runtime.BindStyledParameterWithOptions("simple", "id", chi.URLParam(r, "id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) return diff --git a/internal/test/go.mod b/internal/test/go.mod index 2f09088ea..2e543d054 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -12,8 +12,8 @@ require ( github.com/gofiber/fiber/v2 v2.49.1 github.com/gorilla/mux v1.8.0 github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 - github.com/labstack/echo/v4 v4.11.1 - github.com/oapi-codegen/runtime v1.0.0 + github.com/labstack/echo/v4 v4.11.3 + github.com/oapi-codegen/runtime v1.1.0 github.com/oapi-codegen/testutil v1.0.0 github.com/stretchr/testify v1.8.4 gopkg.in/yaml.v2 v2.4.0 @@ -45,7 +45,7 @@ require ( github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gomarkdown/markdown v0.0.0-20230716120725-531d2d74bc12 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/invopop/yaml v0.2.0 // indirect github.com/iris-contrib/schema v0.0.6 // indirect @@ -90,10 +90,10 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/yosssi/ace v0.0.5 // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.13.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/mod v0.12.0 // indirect - golang.org/x/net v0.15.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/net v0.17.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.12.0 // indirect diff --git a/internal/test/go.sum b/internal/test/go.sum index adfb45357..6a664f0ff 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -78,8 +78,8 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= @@ -120,8 +120,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/labstack/echo/v4 v4.11.1 h1:dEpLU2FLg4UVmvCGPuk/APjlH6GDpbEPti61srUUUs4= -github.com/labstack/echo/v4 v4.11.1/go.mod h1:YuYRTSM3CHs2ybfrL8Px48bO6BAnYIN4l8wSTMP6BDQ= +github.com/labstack/echo/v4 v4.11.3 h1:Upyu3olaqSHkCjs1EJJwQ3WId8b8b1hxbogyommKktM= +github.com/labstack/echo/v4 v4.11.3/go.mod h1:UcGuQ8V6ZNRmSweBIJkPvGfwCMIlFmiqrPqiEBfPYws= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= @@ -152,8 +152,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo= -github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A= +github.com/oapi-codegen/runtime v1.1.0 h1:rJpoNUawn5XTvekgfkvSZr0RqEnoYpFkyvrzfWeFKWM= +github.com/oapi-codegen/runtime v1.1.0/go.mod h1:BeSfBkWWWnAnGdyS+S/GnlbmHKzf8/hwkvelJZDeKA8= github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8= github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= @@ -230,8 +230,8 @@ golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= @@ -240,8 +240,8 @@ golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -256,8 +256,8 @@ golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/internal/test/issues/issue-1180/issue.gen.go b/internal/test/issues/issue-1180/issue.gen.go index 17a9f007b..1b3399abf 100644 --- a/internal/test/issues/issue-1180/issue.gen.go +++ b/internal/test/issues/issue-1180/issue.gen.go @@ -254,7 +254,7 @@ func (w *ServerInterfaceWrapper) GetSimplePrimitive(ctx echo.Context) error { // ------------- Path parameter "param" ------------- var param string - err = runtime.BindStyledParameterWithLocation("simple", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } diff --git a/internal/test/issues/issue-312/issue.gen.go b/internal/test/issues/issue-312/issue.gen.go index 8f857d87b..690705d21 100644 --- a/internal/test/issues/issue-312/issue.gen.go +++ b/internal/test/issues/issue-312/issue.gen.go @@ -440,7 +440,7 @@ func (w *ServerInterfaceWrapper) GetPet(ctx echo.Context) error { // ------------- Path parameter "petId" ------------- var petId string - err = runtime.BindStyledParameterWithLocation("simple", false, "petId", runtime.ParamLocationPath, ctx.Param("petId"), &petId) + err = runtime.BindStyledParameterWithOptions("simple", "petId", ctx.Param("petId"), &petId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter petId: %s", err)) } diff --git a/internal/test/issues/issue-grab_import_names/issue.gen.go b/internal/test/issues/issue-grab_import_names/issue.gen.go index 02f6fb0ad..59924fa3a 100644 --- a/internal/test/issues/issue-grab_import_names/issue.gen.go +++ b/internal/test/issues/issue-grab_import_names/issue.gen.go @@ -304,7 +304,7 @@ func (w *ServerInterfaceWrapper) GetFoo(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Foo, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "Foo", runtime.ParamLocationHeader, valueList[0], &Foo) + err = runtime.BindStyledParameterWithOptions("simple", "Foo", valueList[0], &Foo, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter Foo: %s", err)) } @@ -319,7 +319,7 @@ func (w *ServerInterfaceWrapper) GetFoo(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Bar, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "Bar", runtime.ParamLocationHeader, valueList[0], &Bar) + err = runtime.BindStyledParameterWithOptions("simple", "Bar", valueList[0], &Bar, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter Bar: %s", err)) } diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go index 964e37617..1da8aae1d 100644 --- a/internal/test/parameters/parameters.gen.go +++ b/internal/test/parameters/parameters.gen.go @@ -2776,7 +2776,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("p"); err == nil { var value int32 - err = runtime.BindStyledParameterWithLocation("simple", false, "p", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "p", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter p: %s", err)) } @@ -2787,7 +2787,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("ep"); err == nil { var value int32 - err = runtime.BindStyledParameterWithLocation("simple", true, "ep", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "ep", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ep: %s", err)) } @@ -2798,7 +2798,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("ea"); err == nil { var value []int32 - err = runtime.BindStyledParameterWithLocation("simple", true, "ea", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "ea", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter ea: %s", err)) } @@ -2809,7 +2809,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("a"); err == nil { var value []int32 - err = runtime.BindStyledParameterWithLocation("simple", false, "a", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "a", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter a: %s", err)) } @@ -2820,7 +2820,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("eo"); err == nil { var value Object - err = runtime.BindStyledParameterWithLocation("simple", true, "eo", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "eo", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter eo: %s", err)) } @@ -2831,7 +2831,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("o"); err == nil { var value Object - err = runtime.BindStyledParameterWithLocation("simple", false, "o", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "o", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter o: %s", err)) } @@ -2858,7 +2858,7 @@ func (w *ServerInterfaceWrapper) GetCookie(ctx echo.Context) error { if cookie, err := ctx.Cookie("1s"); err == nil { var value string - err = runtime.BindStyledParameterWithLocation("simple", true, "1s", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "1s", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter 1s: %s", err)) } @@ -2905,7 +2905,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Primitive, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "X-Primitive", runtime.ParamLocationHeader, valueList[0], &XPrimitive) + err = runtime.BindStyledParameterWithOptions("simple", "X-Primitive", valueList[0], &XPrimitive, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Primitive: %s", err)) } @@ -2920,7 +2920,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Primitive-Exploded, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", true, "X-Primitive-Exploded", runtime.ParamLocationHeader, valueList[0], &XPrimitiveExploded) + err = runtime.BindStyledParameterWithOptions("simple", "X-Primitive-Exploded", valueList[0], &XPrimitiveExploded, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Primitive-Exploded: %s", err)) } @@ -2935,7 +2935,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Array-Exploded, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", true, "X-Array-Exploded", runtime.ParamLocationHeader, valueList[0], &XArrayExploded) + err = runtime.BindStyledParameterWithOptions("simple", "X-Array-Exploded", valueList[0], &XArrayExploded, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Array-Exploded: %s", err)) } @@ -2950,7 +2950,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Array, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "X-Array", runtime.ParamLocationHeader, valueList[0], &XArray) + err = runtime.BindStyledParameterWithOptions("simple", "X-Array", valueList[0], &XArray, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Array: %s", err)) } @@ -2965,7 +2965,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Object-Exploded, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", true, "X-Object-Exploded", runtime.ParamLocationHeader, valueList[0], &XObjectExploded) + err = runtime.BindStyledParameterWithOptions("simple", "X-Object-Exploded", valueList[0], &XObjectExploded, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: true, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Object-Exploded: %s", err)) } @@ -2980,7 +2980,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-Object, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "X-Object", runtime.ParamLocationHeader, valueList[0], &XObject) + err = runtime.BindStyledParameterWithOptions("simple", "X-Object", valueList[0], &XObject, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-Object: %s", err)) } @@ -3010,7 +3010,7 @@ func (w *ServerInterfaceWrapper) GetHeader(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for 1-Starting-With-Number, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "1-Starting-With-Number", runtime.ParamLocationHeader, valueList[0], &N1StartingWithNumber) + err = runtime.BindStyledParameterWithOptions("simple", "1-Starting-With-Number", valueList[0], &N1StartingWithNumber, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter 1-Starting-With-Number: %s", err)) } @@ -3029,7 +3029,7 @@ func (w *ServerInterfaceWrapper) GetLabelExplodeArray(ctx echo.Context) error { // ------------- Path parameter "param" ------------- var param []int32 - err = runtime.BindStyledParameterWithLocation("label", true, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("label", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3045,7 +3045,7 @@ func (w *ServerInterfaceWrapper) GetLabelExplodeObject(ctx echo.Context) error { // ------------- Path parameter "param" ------------- var param Object - err = runtime.BindStyledParameterWithLocation("label", true, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("label", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3061,7 +3061,7 @@ func (w *ServerInterfaceWrapper) GetLabelNoExplodeArray(ctx echo.Context) error // ------------- Path parameter "param" ------------- var param []int32 - err = runtime.BindStyledParameterWithLocation("label", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("label", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3077,7 +3077,7 @@ func (w *ServerInterfaceWrapper) GetLabelNoExplodeObject(ctx echo.Context) error // ------------- Path parameter "param" ------------- var param Object - err = runtime.BindStyledParameterWithLocation("label", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("label", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3093,7 +3093,7 @@ func (w *ServerInterfaceWrapper) GetMatrixExplodeArray(ctx echo.Context) error { // ------------- Path parameter "id" ------------- var id []int32 - err = runtime.BindStyledParameterWithLocation("matrix", true, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("matrix", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } @@ -3109,7 +3109,7 @@ func (w *ServerInterfaceWrapper) GetMatrixExplodeObject(ctx echo.Context) error // ------------- Path parameter "id" ------------- var id Object - err = runtime.BindStyledParameterWithLocation("matrix", true, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("matrix", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } @@ -3125,7 +3125,7 @@ func (w *ServerInterfaceWrapper) GetMatrixNoExplodeArray(ctx echo.Context) error // ------------- Path parameter "id" ------------- var id []int32 - err = runtime.BindStyledParameterWithLocation("matrix", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("matrix", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } @@ -3141,7 +3141,7 @@ func (w *ServerInterfaceWrapper) GetMatrixNoExplodeObject(ctx echo.Context) erro // ------------- Path parameter "id" ------------- var id Object - err = runtime.BindStyledParameterWithLocation("matrix", false, "id", runtime.ParamLocationPath, ctx.Param("id"), &id) + err = runtime.BindStyledParameterWithOptions("matrix", "id", ctx.Param("id"), &id, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) } @@ -3268,7 +3268,7 @@ func (w *ServerInterfaceWrapper) GetSimpleExplodeArray(ctx echo.Context) error { // ------------- Path parameter "param" ------------- var param []int32 - err = runtime.BindStyledParameterWithLocation("simple", true, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3284,7 +3284,7 @@ func (w *ServerInterfaceWrapper) GetSimpleExplodeObject(ctx echo.Context) error // ------------- Path parameter "param" ------------- var param Object - err = runtime.BindStyledParameterWithLocation("simple", true, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: true, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3300,7 +3300,7 @@ func (w *ServerInterfaceWrapper) GetSimpleNoExplodeArray(ctx echo.Context) error // ------------- Path parameter "param" ------------- var param []int32 - err = runtime.BindStyledParameterWithLocation("simple", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3316,7 +3316,7 @@ func (w *ServerInterfaceWrapper) GetSimpleNoExplodeObject(ctx echo.Context) erro // ------------- Path parameter "param" ------------- var param Object - err = runtime.BindStyledParameterWithLocation("simple", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } @@ -3332,7 +3332,7 @@ func (w *ServerInterfaceWrapper) GetSimplePrimitive(ctx echo.Context) error { // ------------- Path parameter "param" ------------- var param int32 - err = runtime.BindStyledParameterWithLocation("simple", false, "param", runtime.ParamLocationPath, ctx.Param("param"), ¶m) + err = runtime.BindStyledParameterWithOptions("simple", "param", ctx.Param("param"), ¶m, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter param: %s", err)) } diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go index 91c4e61b2..12c629868 100644 --- a/internal/test/schemas/schemas.gen.go +++ b/internal/test/schemas/schemas.gen.go @@ -1452,7 +1452,7 @@ func (w *ServerInterfaceWrapper) Issue209(ctx echo.Context) error { // ------------- Path parameter "str" ------------- var str StringInPath - err = runtime.BindStyledParameterWithLocation("simple", false, "str", runtime.ParamLocationPath, ctx.Param("str"), &str) + err = runtime.BindStyledParameterWithOptions("simple", "str", ctx.Param("str"), &str, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter str: %s", err)) } @@ -1470,7 +1470,7 @@ func (w *ServerInterfaceWrapper) Issue30(ctx echo.Context) error { // ------------- Path parameter "fallthrough" ------------- var pFallthrough string - err = runtime.BindStyledParameterWithLocation("simple", false, "fallthrough", runtime.ParamLocationPath, ctx.Param("fallthrough"), &pFallthrough) + err = runtime.BindStyledParameterWithOptions("simple", "fallthrough", ctx.Param("fallthrough"), &pFallthrough, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter fallthrough: %s", err)) } @@ -1499,7 +1499,7 @@ func (w *ServerInterfaceWrapper) Issue41(ctx echo.Context) error { // ------------- Path parameter "1param" ------------- var n1param N5StartsWithNumber - err = runtime.BindStyledParameterWithLocation("simple", false, "1param", runtime.ParamLocationPath, ctx.Param("1param"), &n1param) + err = runtime.BindStyledParameterWithOptions("simple", "1param", ctx.Param("1param"), &n1param, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter 1param: %s", err)) } diff --git a/internal/test/server/server.gen.go b/internal/test/server/server.gen.go index f14ceaddf..0620e64c8 100644 --- a/internal/test/server/server.gen.go +++ b/internal/test/server/server.gen.go @@ -309,7 +309,7 @@ func (siw *ServerInterfaceWrapper) GetWithArgs(w http.ResponseWriter, r *http.Re return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header_argument", runtime.ParamLocationHeader, valueList[0], &HeaderArgument) + err = runtime.BindStyledParameterWithOptions("simple", "header_argument", valueList[0], &HeaderArgument, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header_argument", Err: err}) return @@ -339,7 +339,7 @@ func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *h // ------------- Path parameter "global_argument" ------------- var globalArgument int64 - err = runtime.BindStyledParameterWithLocation("simple", false, "global_argument", runtime.ParamLocationPath, chi.URLParam(r, "global_argument"), &globalArgument) + err = runtime.BindStyledParameterWithOptions("simple", "global_argument", chi.URLParam(r, "global_argument"), &globalArgument, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "global_argument", Err: err}) return @@ -348,7 +348,7 @@ func (siw *ServerInterfaceWrapper) GetWithReferences(w http.ResponseWriter, r *h // ------------- Path parameter "argument" ------------- var argument Argument - err = runtime.BindStyledParameterWithLocation("simple", false, "argument", runtime.ParamLocationPath, chi.URLParam(r, "argument"), &argument) + err = runtime.BindStyledParameterWithOptions("simple", "argument", chi.URLParam(r, "argument"), &argument, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "argument", Err: err}) return @@ -374,7 +374,7 @@ func (siw *ServerInterfaceWrapper) GetWithContentType(w http.ResponseWriter, r * // ------------- Path parameter "content_type" ------------- var contentType GetWithContentTypeParamsContentType - err = runtime.BindStyledParameterWithLocation("simple", false, "content_type", runtime.ParamLocationPath, chi.URLParam(r, "content_type"), &contentType) + err = runtime.BindStyledParameterWithOptions("simple", "content_type", chi.URLParam(r, "content_type"), &contentType, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "content_type", Err: err}) return @@ -415,7 +415,7 @@ func (siw *ServerInterfaceWrapper) CreateResource(w http.ResponseWriter, r *http // ------------- Path parameter "argument" ------------- var argument Argument - err = runtime.BindStyledParameterWithLocation("simple", false, "argument", runtime.ParamLocationPath, chi.URLParam(r, "argument"), &argument) + err = runtime.BindStyledParameterWithOptions("simple", "argument", chi.URLParam(r, "argument"), &argument, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "argument", Err: err}) return @@ -441,7 +441,7 @@ func (siw *ServerInterfaceWrapper) CreateResource2(w http.ResponseWriter, r *htt // ------------- Path parameter "inline_argument" ------------- var inlineArgument int - err = runtime.BindStyledParameterWithLocation("simple", false, "inline_argument", runtime.ParamLocationPath, chi.URLParam(r, "inline_argument"), &inlineArgument) + err = runtime.BindStyledParameterWithOptions("simple", "inline_argument", chi.URLParam(r, "inline_argument"), &inlineArgument, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "inline_argument", Err: err}) return @@ -478,7 +478,7 @@ func (siw *ServerInterfaceWrapper) UpdateResource3(w http.ResponseWriter, r *htt // ------------- Path parameter "fallthrough" ------------- var pFallthrough int - err = runtime.BindStyledParameterWithLocation("simple", false, "fallthrough", runtime.ParamLocationPath, chi.URLParam(r, "fallthrough"), &pFallthrough) + err = runtime.BindStyledParameterWithOptions("simple", "fallthrough", chi.URLParam(r, "fallthrough"), &pFallthrough, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "fallthrough", Err: err}) return diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go index 275717b8e..eb64a9bfc 100644 --- a/internal/test/strict-server/chi/server.gen.go +++ b/internal/test/strict-server/chi/server.gen.go @@ -182,7 +182,7 @@ func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(w http.ResponseWr // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameterWithLocation("simple", false, "type", runtime.ParamLocationPath, chi.URLParam(r, "type"), &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", chi.URLParam(r, "type"), &pType, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "type", Err: err}) return @@ -294,7 +294,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(w http.ResponseWriter, r *http return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header1", Err: err}) return @@ -317,7 +317,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(w http.ResponseWriter, r *http return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header2", Err: err}) return diff --git a/internal/test/strict-server/echo/server.gen.go b/internal/test/strict-server/echo/server.gen.go index eebb21805..af647a026 100644 --- a/internal/test/strict-server/echo/server.gen.go +++ b/internal/test/strict-server/echo/server.gen.go @@ -98,7 +98,7 @@ func (w *ServerInterfaceWrapper) ReservedGoKeywordParameters(ctx echo.Context) e // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameterWithLocation("simple", false, "type", runtime.ParamLocationPath, ctx.Param("type"), &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", ctx.Param("type"), &pType, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter type: %s", err)) } @@ -169,7 +169,7 @@ func (w *ServerInterfaceWrapper) HeadersExample(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for header1, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter header1: %s", err)) } @@ -186,7 +186,7 @@ func (w *ServerInterfaceWrapper) HeadersExample(ctx echo.Context) error { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for header2, got %d", n)) } - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter header2: %s", err)) } diff --git a/internal/test/strict-server/fiber/server.gen.go b/internal/test/strict-server/fiber/server.gen.go index 76b2e1e26..3e86046b5 100644 --- a/internal/test/strict-server/fiber/server.gen.go +++ b/internal/test/strict-server/fiber/server.gen.go @@ -92,7 +92,7 @@ func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(c *fiber.Ctx) err // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameter("simple", false, "type", c.Params("type"), &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", c.Params("type"), &pType, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter type: %w", err).Error()) } @@ -144,7 +144,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(c *fiber.Ctx) error { if value, found := headers[http.CanonicalHeaderKey("header1")]; found { var Header1 string - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, value, &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", value, &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter header1: %w", err).Error()) } @@ -160,7 +160,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(c *fiber.Ctx) error { if value, found := headers[http.CanonicalHeaderKey("header2")]; found { var Header2 int - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, value, &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", value, &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter header2: %w", err).Error()) } diff --git a/internal/test/strict-server/gin/server.gen.go b/internal/test/strict-server/gin/server.gen.go index 4504814ad..d08ad6004 100644 --- a/internal/test/strict-server/gin/server.gen.go +++ b/internal/test/strict-server/gin/server.gen.go @@ -116,7 +116,7 @@ func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(c *gin.Context) { // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameter("simple", false, "type", c.Param("type"), &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", c.Param("type"), &pType, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter type: %w", err), http.StatusBadRequest) return @@ -216,7 +216,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(c *gin.Context) { return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter header1: %w", err), http.StatusBadRequest) return @@ -238,7 +238,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(c *gin.Context) { return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter header2: %w", err), http.StatusBadRequest) return diff --git a/internal/test/strict-server/gorilla/server.gen.go b/internal/test/strict-server/gorilla/server.gen.go index d8d324626..fcf9c712f 100644 --- a/internal/test/strict-server/gorilla/server.gen.go +++ b/internal/test/strict-server/gorilla/server.gen.go @@ -123,7 +123,7 @@ func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(w http.ResponseWr // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameter("simple", false, "type", mux.Vars(r)["type"], &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", mux.Vars(r)["type"], &pType, runtime.BindStyledParameterOptions{Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "type", Err: err}) return @@ -235,7 +235,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(w http.ResponseWriter, r *http return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header1", Err: err}) return @@ -258,7 +258,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(w http.ResponseWriter, r *http return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "header2", Err: err}) return diff --git a/internal/test/strict-server/iris/server.gen.go b/internal/test/strict-server/iris/server.gen.go index 8494e426b..fe649bc0c 100644 --- a/internal/test/strict-server/iris/server.gen.go +++ b/internal/test/strict-server/iris/server.gen.go @@ -96,7 +96,7 @@ func (w *ServerInterfaceWrapper) ReservedGoKeywordParameters(ctx iris.Context) { // ------------- Path parameter "type" ------------- var pType string - err = runtime.BindStyledParameterWithLocation("simple", false, "type", runtime.ParamLocationPath, ctx.Params().Get("type"), &pType) + err = runtime.BindStyledParameterWithOptions("simple", "type", ctx.Params().Get("type"), &pType, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter type: %s", err) @@ -161,7 +161,7 @@ func (w *ServerInterfaceWrapper) HeadersExample(ctx iris.Context) { return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, valueList[0], &Header1) + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter header1: %s", err) @@ -184,7 +184,7 @@ func (w *ServerInterfaceWrapper) HeadersExample(ctx iris.Context) { return } - err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, valueList[0], &Header2) + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter header2: %s", err) diff --git a/pkg/codegen/templates/chi/chi-middleware.tmpl b/pkg/codegen/templates/chi/chi-middleware.tmpl index 313122a36..2e84418f9 100644 --- a/pkg/codegen/templates/chi/chi-middleware.tmpl +++ b/pkg/codegen/templates/chi/chi-middleware.tmpl @@ -30,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, chi.URLParam(r, "{{.ParamName}}"), &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", chi.URLParam(r, "{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -107,7 +107,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -155,7 +155,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return diff --git a/pkg/codegen/templates/echo/echo-wrappers.tmpl b/pkg/codegen/templates/echo/echo-wrappers.tmpl index 21ba626ca..1603c3d5c 100644 --- a/pkg/codegen/templates/echo/echo-wrappers.tmpl +++ b/pkg/codegen/templates/echo/echo-wrappers.tmpl @@ -18,7 +18,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, ctx.Param("{{.ParamName}}"), &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } @@ -79,7 +79,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } @@ -111,7 +111,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx echo.Context) error { {{end}} {{if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithLocation("simple",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter {{.ParamName}}: %s", err)) } diff --git a/pkg/codegen/templates/fiber/fiber-middleware.tmpl b/pkg/codegen/templates/fiber/fiber-middleware.tmpl index ad3160d73..0904c7894 100644 --- a/pkg/codegen/templates/fiber/fiber-middleware.tmpl +++ b/pkg/codegen/templates/fiber/fiber-middleware.tmpl @@ -27,7 +27,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -104,7 +104,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, value, &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", value, &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } @@ -147,7 +147,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error { {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error()) } diff --git a/pkg/codegen/templates/gin/gin-wrappers.tmpl b/pkg/codegen/templates/gin/gin-wrappers.tmpl index b7332b3c3..8f1a6b229 100644 --- a/pkg/codegen/templates/gin/gin-wrappers.tmpl +++ b/pkg/codegen/templates/gin/gin-wrappers.tmpl @@ -30,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", c.Param("{{.ParamName}}"), &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", c.Param("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -108,7 +108,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return @@ -155,7 +155,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) { {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err), http.StatusBadRequest) return diff --git a/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl index 0e0a5e17c..9b4068790 100644 --- a/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl +++ b/pkg/codegen/templates/gorilla/gorilla-middleware.tmpl @@ -30,7 +30,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", mux.Vars(r)["{{.ParamName}}"], &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", mux.Vars(r)["{{.ParamName}}"], &{{$varName}}, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -107,7 +107,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return @@ -155,7 +155,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(w http.ResponseWriter, r *http.Requ {{- if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "{{.ParamName}}", Err: err}) return diff --git a/pkg/codegen/templates/iris/iris-middleware.tmpl b/pkg/codegen/templates/iris/iris-middleware.tmpl index 6f0b949a4..ae40439ef 100644 --- a/pkg/codegen/templates/iris/iris-middleware.tmpl +++ b/pkg/codegen/templates/iris/iris-middleware.tmpl @@ -25,7 +25,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationPath, ctx.Params().Get("{{.ParamName}}"), &{{$varName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", ctx.Params().Get("{{.ParamName}}"), &{{$varName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) @@ -98,7 +98,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { } {{end}} {{if .IsStyled}} - err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, valueList[0], &{{.GoName}}) + err = runtime.BindStyledParameterWithOptions("{{.Style}}", "{{.ParamName}}", valueList[0], &{{.GoName}}, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) @@ -138,7 +138,7 @@ func (w *ServerInterfaceWrapper) {{.OperationId}} (ctx iris.Context) { {{end}} {{if .IsStyled}} var value {{.TypeDef}} - err = runtime.BindStyledParameterWithLocation("simple",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationCookie, cookie.Value, &value) + err = runtime.BindStyledParameterWithOptions("simple", "{{.ParamName}}", cookie.Value, &value, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationCookie, Explode: {{.Explode}}, Required: {{.Required}}}) if err != nil { ctx.StatusCode(http.StatusBadRequest) ctx.Writef("Invalid format for parameter {{.ParamName}}: %s", err) From 7e5100edce01676dd12162845861b5887184b1cd Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 21 Nov 2023 00:59:56 +0900 Subject: [PATCH 03/25] Fix typo in README.md (#1351) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5a467e475..14bc53873 100644 --- a/README.md +++ b/README.md @@ -933,7 +933,7 @@ output-options: # using a local file client-with-responses.tmpl: /home/username/workspace/templatesProject/my-client-with-responses.tmpl - # The following are referencing a versuion of the default + # The following are referencing a version of the default # client-with-responses.tmpl file, but loaded in through # github's raw.githubusercontent.com. The general form # to use raw.githubusercontent.com is as follows From add3126ee845df3c36b7f0611615f033662fa717 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:02:25 +0000 Subject: [PATCH 04/25] Update module golang.org/x/text to v0.14.0 (#1349) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- examples/go.mod | 2 +- examples/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/test/go.mod | 2 +- internal/test/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index 68852eafa..a2dd5ad71 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -106,7 +106,7 @@ require ( golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.12.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/examples/go.sum b/examples/go.sum index 745725b5f..e0d7cbf9d 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -313,8 +313,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/go.mod b/go.mod index 0ac670916..954093adc 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/getkin/kin-openapi v0.118.0 github.com/stretchr/testify v1.8.4 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 - golang.org/x/text v0.13.0 + golang.org/x/text v0.14.0 golang.org/x/tools v0.12.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index d75c76e88..f1f4744ca 100644 --- a/go.sum +++ b/go.sum @@ -59,8 +59,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= diff --git a/internal/test/go.mod b/internal/test/go.mod index 2e543d054..13fcf07b4 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -94,7 +94,7 @@ require ( golang.org/x/mod v0.12.0 // indirect golang.org/x/net v0.17.0 // indirect golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.12.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/internal/test/go.sum b/internal/test/go.sum index 6a664f0ff..9bc476772 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -262,8 +262,8 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 4ed71d34f9e9403aded664866a9d136f7eec4212 Mon Sep 17 00:00:00 2001 From: Krisztian Gacsal Date: Fri, 1 Dec 2023 00:19:43 +0100 Subject: [PATCH 05/25] feat: bump kin-openapi to v0.119.0 --- examples/go.mod | 2 +- examples/go.sum | 16 ++-------------- go.mod | 12 +++++------- go.sum | 43 ++++++++++++++++++------------------------- internal/test/go.mod | 2 +- internal/test/go.sum | 16 ++-------------- 6 files changed, 29 insertions(+), 62 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index a2dd5ad71..4cf8b87ff 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.118.0 + github.com/getkin/kin-openapi v0.120.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/examples/go.sum b/examples/go.sum index e0d7cbf9d..4550d368a 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -42,18 +42,16 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -64,7 +62,6 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -89,7 +86,6 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go= @@ -118,7 +114,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -144,8 +139,6 @@ github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNB github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -184,7 +177,6 @@ github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4 github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -226,8 +218,6 @@ github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -334,12 +324,10 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.mod b/go.mod index 954093adc..643b3e035 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/deepmap/oapi-codegen/v2 go 1.20 require ( - github.com/getkin/kin-openapi v0.118.0 + github.com/getkin/kin-openapi v0.120.0 github.com/stretchr/testify v1.8.4 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/text v0.14.0 @@ -13,18 +13,16 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/swag v0.19.5 // indirect - github.com/invopop/yaml v0.1.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - github.com/perimeterx/marshmallow v1.1.4 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/mod v0.12.0 // indirect golang.org/x/sys v0.12.0 // indirect - gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f1f4744ca..b97886e41 100644 --- a/go.sum +++ b/go.sum @@ -1,46 +1,41 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/invopop/yaml v0.1.0 h1:YW3WGUoJEXYfzWBjn00zIlrw7brGVD0fUKRYDPAPhrc= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -66,10 +61,8 @@ golang.org/x/tools v0.12.0 h1:YW6HUoUmYBpwSgyaGaZq1fHjrBjX1rlpZ54T6mu2kss= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/test/go.mod b/internal/test/go.mod index 13fcf07b4..a26e47bc1 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.118.0 + github.com/getkin/kin-openapi v0.120.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/internal/test/go.sum b/internal/test/go.sum index 9bc476772..586b3b373 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -39,18 +39,16 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.118.0 h1:z43njxPmJ7TaPpMSCQb7PN0dEYno4tyBPQcrFdHoLuM= -github.com/getkin/kin-openapi v0.118.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc= +github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= +github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -61,7 +59,6 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= @@ -86,7 +83,6 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= -github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go= @@ -115,7 +111,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02 github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -128,8 +123,6 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= @@ -158,7 +151,6 @@ github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4 github.com/oapi-codegen/testutil v1.0.0/go.mod h1:ttCaYbHvJtHuiyeBF0tPIX+4uhEPTeizXKx28okijLw= github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -199,8 +191,6 @@ github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= @@ -280,12 +270,10 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 6aa5167b9d092c51c8724d734a80814a06210629 Mon Sep 17 00:00:00 2001 From: Krisztian Gacsal Date: Fri, 1 Dec 2023 00:19:52 +0100 Subject: [PATCH 06/25] feat: bump kin-openapi to v0.121.0 --- examples/go.mod | 2 +- examples/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/test/go.mod | 2 +- internal/test/go.sum | 4 ++-- pkg/codegen/codegen.go | 5 +++-- pkg/codegen/operations.go | 5 +++-- pkg/codegen/utils.go | 2 +- 9 files changed, 16 insertions(+), 14 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index 4cf8b87ff..da2c8474d 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.120.0 + github.com/getkin/kin-openapi v0.121.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/examples/go.sum b/examples/go.sum index 4550d368a..32a4ad9e6 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -42,8 +42,8 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= -github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= +github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= diff --git a/go.mod b/go.mod index 643b3e035..d185e0224 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/deepmap/oapi-codegen/v2 go 1.20 require ( - github.com/getkin/kin-openapi v0.120.0 + github.com/getkin/kin-openapi v0.121.0 github.com/stretchr/testify v1.8.4 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/text v0.14.0 diff --git a/go.sum b/go.sum index b97886e41..4872066d7 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= -github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= +github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= diff --git a/internal/test/go.mod b/internal/test/go.mod index a26e47bc1..59cf87bfe 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.120.0 + github.com/getkin/kin-openapi v0.121.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/internal/test/go.sum b/internal/test/go.sum index 586b3b373..ae0ba5194 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -39,8 +39,8 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= -github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= +github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index 09bf08317..f4942eae0 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -30,9 +30,10 @@ import ( "text/template" "time" - "github.com/deepmap/oapi-codegen/v2/pkg/util" "github.com/getkin/kin-openapi/openapi3" "golang.org/x/tools/imports" + + "github.com/deepmap/oapi-codegen/v2/pkg/util" ) // Embed the templates directory @@ -557,7 +558,7 @@ func GenerateTypesForParameters(t *template.Template, params map[string]*openapi // GenerateTypesForResponses generates type definitions for any custom types defined in the // components/responses section of the Swagger spec. -func GenerateTypesForResponses(t *template.Template, responses openapi3.Responses) ([]TypeDefinition, error) { +func GenerateTypesForResponses(t *template.Template, responses openapi3.ResponseBodies) ([]TypeDefinition, error) { var types []TypeDefinition for _, responseName := range SortedResponsesKeys(responses) { diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go index dd287bc80..336fb6090 100644 --- a/pkg/codegen/operations.go +++ b/pkg/codegen/operations.go @@ -23,8 +23,9 @@ import ( "text/template" "unicode" - "github.com/deepmap/oapi-codegen/v2/pkg/util" "github.com/getkin/kin-openapi/openapi3" + + "github.com/deepmap/oapi-codegen/v2/pkg/util" ) type ParameterDefinition struct { @@ -762,7 +763,7 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody return bodyDefinitions, typeDefinitions, nil } -func GenerateResponseDefinitions(operationID string, responses openapi3.Responses) ([]ResponseDefinition, error) { +func GenerateResponseDefinitions(operationID string, responses map[string]*openapi3.ResponseRef) ([]ResponseDefinition, error) { var responseDefinitions []ResponseDefinition // do not let multiple status codes ref to same response, it will break the type switch refSet := make(map[string]struct{}) diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go index 6494248c7..e33adf34e 100644 --- a/pkg/codegen/utils.go +++ b/pkg/codegen/utils.go @@ -229,7 +229,7 @@ func SortedOperationsKeys(dict map[string]*openapi3.Operation) []string { } // SortedResponsesKeys returns Responses dictionary keys in sorted order -func SortedResponsesKeys(dict openapi3.Responses) []string { +func SortedResponsesKeys(dict map[string]*openapi3.ResponseRef) []string { keys := make([]string, len(dict)) i := 0 for key := range dict { From 1f53862bcc64573d3d0c4c105c71a8143e7b1816 Mon Sep 17 00:00:00 2001 From: Krisztian Gacsal Date: Fri, 1 Dec 2023 00:19:55 +0100 Subject: [PATCH 07/25] feat: bump kin-openapi to v0.122.0 --- examples/go.mod | 2 +- examples/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- internal/test/go.mod | 2 +- internal/test/go.sum | 4 ++-- pkg/codegen/filter.go | 10 +++++++--- pkg/codegen/operations.go | 19 +++++++++++++------ pkg/codegen/prune.go | 12 +++++++----- pkg/codegen/prune_test.go | 12 ++++++------ pkg/codegen/template_helpers.go | 7 ++++--- pkg/codegen/utils.go | 2 +- pkg/codegen/utils_test.go | 4 ++-- 13 files changed, 49 insertions(+), 35 deletions(-) diff --git a/examples/go.mod b/examples/go.mod index da2c8474d..acb387490 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.121.0 + github.com/getkin/kin-openapi v0.122.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/examples/go.sum b/examples/go.sum index 32a4ad9e6..48c332c54 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -42,8 +42,8 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= -github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= +github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= diff --git a/go.mod b/go.mod index d185e0224..6ebe220a7 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/deepmap/oapi-codegen/v2 go 1.20 require ( - github.com/getkin/kin-openapi v0.121.0 + github.com/getkin/kin-openapi v0.122.0 github.com/stretchr/testify v1.8.4 golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/text v0.14.0 diff --git a/go.sum b/go.sum index 4872066d7..fd90bfcab 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= -github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= +github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= diff --git a/internal/test/go.mod b/internal/test/go.mod index 59cf87bfe..8e8c37ec3 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -6,7 +6,7 @@ replace github.com/deepmap/oapi-codegen/v2 => ../../ require ( github.com/deepmap/oapi-codegen/v2 v2.0.0-00010101000000-000000000000 - github.com/getkin/kin-openapi v0.121.0 + github.com/getkin/kin-openapi v0.122.0 github.com/gin-gonic/gin v1.9.1 github.com/go-chi/chi/v5 v5.0.10 github.com/gofiber/fiber/v2 v2.49.1 diff --git a/internal/test/go.sum b/internal/test/go.sum index ae0ba5194..450aa4a39 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -39,8 +39,8 @@ github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0H github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/getkin/kin-openapi v0.121.0 h1:KbQmTugy+lQF+ed5H3tikjT4prqx5+KCLAq4U81Hkcw= -github.com/getkin/kin-openapi v0.121.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= +github.com/getkin/kin-openapi v0.122.0 h1:WB9Jbl0Hp/T79/JF9xlSW5Kl9uYdk/AWD0yAd9HOM10= +github.com/getkin/kin-openapi v0.122.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= diff --git a/pkg/codegen/filter.go b/pkg/codegen/filter.go index 01a363957..a969badc7 100644 --- a/pkg/codegen/filter.go +++ b/pkg/codegen/filter.go @@ -11,12 +11,16 @@ func filterOperationsByTag(swagger *openapi3.T, opts Configuration) { } } -func excludeOperationsWithTags(paths openapi3.Paths, tags []string) { +func excludeOperationsWithTags(paths *openapi3.Paths, tags []string) { includeOperationsWithTags(paths, tags, true) } -func includeOperationsWithTags(paths openapi3.Paths, tags []string, exclude bool) { - for _, pathItem := range paths { +func includeOperationsWithTags(paths *openapi3.Paths, tags []string, exclude bool) { + if paths == nil { + return + } + + for _, pathItem := range paths.Map() { ops := pathItem.Operations() names := make([]string, 0, len(ops)) for name, op := range ops { diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go index 336fb6090..950186f6f 100644 --- a/pkg/codegen/operations.go +++ b/pkg/codegen/operations.go @@ -275,10 +275,13 @@ func (o *OperationDefinition) SummaryAsComment() string { func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefinition, error) { var tds []ResponseTypeDefinition - responses := o.Spec.Responses - sortedResponsesKeys := SortedResponsesKeys(responses) + if o.Spec == nil || o.Spec.Responses == nil { + return tds, nil + } + + sortedResponsesKeys := SortedResponsesKeys(o.Spec.Responses.Map()) for _, responseName := range sortedResponsesKeys { - responseRef := responses[responseName] + responseRef := o.Spec.Responses.Value(responseName) // We can only generate a type if we have a value: if responseRef.Value != nil { @@ -537,8 +540,12 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper toCamelCaseFunc = ToCamelCase } - for _, requestPath := range SortedPathsKeys(swagger.Paths) { - pathItem := swagger.Paths[requestPath] + if swagger == nil || swagger.Paths == nil { + return operations, nil + } + + for _, requestPath := range SortedPathsKeys(swagger.Paths.Map()) { + pathItem := swagger.Paths.Value(requestPath) // These are parameters defined for all methods on a given path. They // are shared by all methods. globalParams, err := DescribeParameters(pathItem.Parameters, nil) @@ -594,7 +601,7 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper return nil, fmt.Errorf("error generating body definitions: %w", err) } - responseDefinitions, err := GenerateResponseDefinitions(op.OperationID, op.Responses) + responseDefinitions, err := GenerateResponseDefinitions(op.OperationID, op.Responses.Map()) if err != nil { return nil, fmt.Errorf("error generating response definitions: %w", err) } diff --git a/pkg/codegen/prune.go b/pkg/codegen/prune.go index 9dc4fd871..0d3a88974 100644 --- a/pkg/codegen/prune.go +++ b/pkg/codegen/prune.go @@ -22,11 +22,11 @@ type RefWrapper struct { } func walkSwagger(swagger *openapi3.T, doFn func(RefWrapper) (bool, error)) error { - if swagger == nil { + if swagger == nil || swagger.Paths == nil { return nil } - for _, p := range swagger.Paths { + for _, p := range swagger.Paths.Map() { for _, param := range p.Parameters { _ = walkParameterRef(param, doFn) } @@ -52,8 +52,10 @@ func walkOperation(op *openapi3.Operation, doFn func(RefWrapper) (bool, error)) _ = walkRequestBodyRef(op.RequestBody, doFn) - for _, response := range op.Responses { - _ = walkResponseRef(response, doFn) + if op.Responses != nil { + for _, response := range op.Responses.Map() { + _ = walkResponseRef(response, doFn) + } } for _, callback := range op.Callbacks { @@ -273,7 +275,7 @@ func walkCallbackRef(ref *openapi3.CallbackRef, doFn func(RefWrapper) (bool, err return nil } - for _, pathItem := range *ref.Value { + for _, pathItem := range ref.Value.Map() { for _, parameter := range pathItem.Parameters { _ = walkParameterRef(parameter, doFn) } diff --git a/pkg/codegen/prune_test.go b/pkg/codegen/prune_test.go index e0cc16c08..9972f5bac 100644 --- a/pkg/codegen/prune_test.go +++ b/pkg/codegen/prune_test.go @@ -67,9 +67,9 @@ func TestFilterOnlyCat(t *testing.T) { refs = findComponentRefs(swagger) assert.Len(t, refs, 7) - assert.NotEmpty(t, swagger.Paths["/cat"], "/cat path should still be in spec") - assert.NotEmpty(t, swagger.Paths["/cat"].Get, "GET /cat operation should still be in spec") - assert.Empty(t, swagger.Paths["/dog"].Get, "GET /dog should have been removed from spec") + assert.NotEmpty(t, swagger.Paths.Value("/cat"), "/cat path should still be in spec") + assert.NotEmpty(t, swagger.Paths.Value("/cat").Get, "GET /cat operation should still be in spec") + assert.Empty(t, swagger.Paths.Value("/dog").Get, "GET /dog should have been removed from spec") pruneUnusedComponents(swagger) @@ -97,9 +97,9 @@ func TestFilterOnlyDog(t *testing.T) { assert.Len(t, swagger.Components.Schemas, 5) - assert.NotEmpty(t, swagger.Paths["/dog"]) - assert.NotEmpty(t, swagger.Paths["/dog"].Get) - assert.Empty(t, swagger.Paths["/cat"].Get) + assert.NotEmpty(t, swagger.Paths.Value("/dog")) + assert.NotEmpty(t, swagger.Paths.Value("/dog").Get) + assert.Empty(t, swagger.Paths.Value("/cat").Get) pruneUnusedComponents(swagger) diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go index 87394c894..79cf4cb35 100644 --- a/pkg/codegen/template_helpers.go +++ b/pkg/codegen/template_helpers.go @@ -20,9 +20,10 @@ import ( "strings" "text/template" - "github.com/deepmap/oapi-codegen/v2/pkg/util" "golang.org/x/text/cases" "golang.org/x/text/language" + + "github.com/deepmap/oapi-codegen/v2/pkg/util" ) const ( @@ -122,8 +123,8 @@ func genResponseUnmarshal(op *OperationDefinition) string { responses := op.Spec.Responses for _, typeDefinition := range typeDefinitions { - responseRef, ok := responses[typeDefinition.ResponseName] - if !ok { + responseRef := responses.Value(typeDefinition.ResponseName) + if responseRef == nil { continue } diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go index e33adf34e..49a2f53f2 100644 --- a/pkg/codegen/utils.go +++ b/pkg/codegen/utils.go @@ -205,7 +205,7 @@ func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string { // SortedPathsKeys is the same as above, except it sorts the keys for a Paths // dictionary. -func SortedPathsKeys(dict openapi3.Paths) []string { +func SortedPathsKeys(dict map[string]*openapi3.PathItem) []string { keys := make([]string, len(dict)) i := 0 for key := range dict { diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go index 8806c6adc..c837ffe7e 100644 --- a/pkg/codegen/utils_test.go +++ b/pkg/codegen/utils_test.go @@ -44,7 +44,7 @@ func TestSortedSchemaKeys(t *testing.T) { } func TestSortedPathsKeys(t *testing.T) { - dict := openapi3.Paths{ + dict := map[string]*openapi3.PathItem{ "f": nil, "c": nil, "b": nil, @@ -74,7 +74,7 @@ func TestSortedOperationsKeys(t *testing.T) { } func TestSortedResponsesKeys(t *testing.T) { - dict := openapi3.Responses{ + dict := map[string]*openapi3.ResponseRef{ "f": nil, "c": nil, "b": nil, From 039ba0ecd102d0d533b6333dbdca294a16edc42e Mon Sep 17 00:00:00 2001 From: ShouheiNishi <96609867+ShouheiNishi@users.noreply.github.com> Date: Wed, 13 Dec 2023 21:14:56 +0900 Subject: [PATCH 08/25] Fix: Handle strict server handling for all multipart media types (#1385) Right now, we're only generating code for multipart requests when the media type is a `multipart/form-data`, which means that other multipart types, like `multipart/related` do not correctly generate. We should amend this, generating code that validates the media type provided and handling the request. Closes #1381. --- .../test/issues/issue-1212/pkg1/pkg1.gen.go | 3 +- internal/test/strict-server/chi/server.gen.go | 134 +++++++++++++++--- internal/test/strict-server/chi/server.go | 24 ++++ internal/test/strict-server/chi/types.gen.go | 3 + .../test/strict-server/client/client.gen.go | 96 +++++++++++++ .../test/strict-server/echo/server.gen.go | 118 ++++++++++++--- internal/test/strict-server/echo/server.go | 24 ++++ internal/test/strict-server/echo/types.gen.go | 3 + .../test/strict-server/fiber/server.gen.go | 118 ++++++++++++--- internal/test/strict-server/fiber/server.go | 24 ++++ .../test/strict-server/fiber/types.gen.go | 3 + internal/test/strict-server/gin/server.gen.go | 126 +++++++++++++--- internal/test/strict-server/gin/server.go | 24 ++++ internal/test/strict-server/gin/types.gen.go | 3 + .../test/strict-server/gorilla/server.gen.go | 128 ++++++++++++++--- .../test/strict-server/gorilla/types.gen.go | 3 + .../test/strict-server/iris/server.gen.go | 122 +++++++++++++--- internal/test/strict-server/iris/server.go | 24 ++++ internal/test/strict-server/iris/types.gen.go | 3 + .../test/strict-server/strict-schema.yaml | 19 +++ internal/test/strict-server/strict_test.go | 24 ++++ pkg/codegen/templates/imports.tmpl | 2 + pkg/codegen/templates/strict/strict-echo.tmpl | 10 ++ .../strict/strict-fiber-interface.tmpl | 2 +- .../templates/strict/strict-fiber.tmpl | 10 ++ pkg/codegen/templates/strict/strict-gin.tmpl | 12 ++ pkg/codegen/templates/strict/strict-http.tmpl | 12 ++ .../templates/strict/strict-interface.tmpl | 2 +- .../strict/strict-iris-interface.tmpl | 2 +- pkg/codegen/templates/strict/strict-iris.tmpl | 12 ++ 30 files changed, 984 insertions(+), 106 deletions(-) diff --git a/internal/test/issues/issue-1212/pkg1/pkg1.gen.go b/internal/test/issues/issue-1212/pkg1/pkg1.gen.go index d7a20ece2..4f0e619c3 100644 --- a/internal/test/issues/issue-1212/pkg1/pkg1.gen.go +++ b/internal/test/issues/issue-1212/pkg1/pkg1.gen.go @@ -10,6 +10,7 @@ import ( "encoding/base64" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -301,7 +302,7 @@ type Test200MultipartResponse externalRef0.TestMultipartResponse func (response Test200MultipartResponse) VisitTestResponse(w http.ResponseWriter) error { writer := multipart.NewWriter(w) - w.Header().Set("Content-Type", writer.FormDataContentType()) + w.Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) w.WriteHeader(200) defer writer.Close() diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go index eb64a9bfc..6a720b699 100644 --- a/internal/test/strict-server/chi/server.gen.go +++ b/internal/test/strict-server/chi/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -32,6 +33,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(w http.ResponseWriter, r *http.Request) + // (POST /multipart-related) + MultipartRelatedExample(w http.ResponseWriter, r *http.Request) + // (POST /multiple) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) @@ -74,6 +78,11 @@ func (_ Unimplemented) MultipartExample(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusNotImplemented) } +// (POST /multipart-related) +func (_ Unimplemented) MultipartRelatedExample(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // (POST /multiple) func (_ Unimplemented) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) @@ -158,6 +167,21 @@ func (siw *ServerInterfaceWrapper) MultipartExample(w http.ResponseWriter, r *ht handler.ServeHTTP(w, r.WithContext(ctx)) } +// MultipartRelatedExample operation middleware +func (siw *ServerInterfaceWrapper) MultipartRelatedExample(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.MultipartRelatedExample(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // MultipleRequestAndResponseTypes operation middleware func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -472,6 +496,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/multipart", wrapper.MultipartExample) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes) }) @@ -584,6 +611,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + w.Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -1007,6 +1069,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -1126,6 +1191,40 @@ func (sh *strictHandler) MultipartExample(w http.ResponseWriter, r *http.Request } } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(w http.ResponseWriter, r *http.Request) { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, err) + return + } else if boundary := params["boundary"]; boundary == "" { + sh.options.RequestErrorHandlerFunc(w, r, http.ErrMissingBoundary) + return + } else { + request.Body = multipart.NewReader(r.Body, boundary) + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx, request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + if err := validResponse.VisitMultipartRelatedExampleResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) { var request MultipleRequestAndResponseTypesRequestObject @@ -1437,23 +1536,24 @@ func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/chi/server.go b/internal/test/strict-server/chi/server.go index 83eb50339..fe744e250 100644 --- a/internal/test/strict-server/chi/server.go +++ b/internal/test/strict-server/chi/server.go @@ -41,6 +41,30 @@ func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExa }), nil } +func (s StrictServer) MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) { + return MultipartRelatedExample200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.Body.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + w, err := writer.CreatePart(part.Header) + if err != nil { + return err + } + _, err = io.Copy(w, part) + if err != nil { + return err + } + if err = part.Close(); err != nil { + return err + } + } + }), nil +} + func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) { switch { case request.Body != nil: diff --git a/internal/test/strict-server/chi/types.gen.go b/internal/test/strict-server/chi/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/chi/types.gen.go +++ b/internal/test/strict-server/chi/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/client/client.gen.go b/internal/test/strict-server/client/client.gen.go index dcf521d72..16e588bd6 100644 --- a/internal/test/strict-server/client/client.gen.go +++ b/internal/test/strict-server/client/client.gen.go @@ -42,6 +42,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example @@ -150,6 +153,9 @@ type ClientInterface interface { // MultipartExampleWithBody request with any body MultipartExampleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MultipartRelatedExampleWithBody request with any body + MultipartRelatedExampleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + // MultipleRequestAndResponseTypesWithBody request with any body MultipleRequestAndResponseTypesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -230,6 +236,18 @@ func (c *Client) MultipartExampleWithBody(ctx context.Context, contentType strin return c.Client.Do(req) } +func (c *Client) MultipartRelatedExampleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMultipartRelatedExampleRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) MultipleRequestAndResponseTypesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewMultipleRequestAndResponseTypesRequestWithBody(c.Server, contentType, body) if err != nil { @@ -503,6 +521,35 @@ func NewMultipartExampleRequestWithBody(server string, contentType string, body return req, nil } +// NewMultipartRelatedExampleRequestWithBody generates requests for MultipartRelatedExample with any type of body +func NewMultipartRelatedExampleRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/multipart-related") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewMultipleRequestAndResponseTypesRequest calls the generic MultipleRequestAndResponseTypes builder with application/json body func NewMultipleRequestAndResponseTypesRequest(server string, body MultipleRequestAndResponseTypesJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -924,6 +971,9 @@ type ClientWithResponsesInterface interface { // MultipartExampleWithBodyWithResponse request with any body MultipartExampleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MultipartExampleResponse, error) + // MultipartRelatedExampleWithBodyWithResponse request with any body + MultipartRelatedExampleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MultipartRelatedExampleResponse, error) + // MultipleRequestAndResponseTypesWithBodyWithResponse request with any body MultipleRequestAndResponseTypesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MultipleRequestAndResponseTypesResponse, error) @@ -1011,6 +1061,27 @@ func (r MultipartExampleResponse) StatusCode() int { return 0 } +type MultipartRelatedExampleResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r MultipartRelatedExampleResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MultipartRelatedExampleResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type MultipleRequestAndResponseTypesResponse struct { Body []byte HTTPResponse *http.Response @@ -1233,6 +1304,15 @@ func (c *ClientWithResponses) MultipartExampleWithBodyWithResponse(ctx context.C return ParseMultipartExampleResponse(rsp) } +// MultipartRelatedExampleWithBodyWithResponse request with arbitrary body returning *MultipartRelatedExampleResponse +func (c *ClientWithResponses) MultipartRelatedExampleWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MultipartRelatedExampleResponse, error) { + rsp, err := c.MultipartRelatedExampleWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMultipartRelatedExampleResponse(rsp) +} + // MultipleRequestAndResponseTypesWithBodyWithResponse request with arbitrary body returning *MultipleRequestAndResponseTypesResponse func (c *ClientWithResponses) MultipleRequestAndResponseTypesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MultipleRequestAndResponseTypesResponse, error) { rsp, err := c.MultipleRequestAndResponseTypesWithBody(ctx, contentType, body, reqEditors...) @@ -1420,6 +1500,22 @@ func ParseMultipartExampleResponse(rsp *http.Response) (*MultipartExampleRespons return response, nil } +// ParseMultipartRelatedExampleResponse parses an HTTP response from a MultipartRelatedExampleWithResponse call +func ParseMultipartRelatedExampleResponse(rsp *http.Response) (*MultipartRelatedExampleResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &MultipartRelatedExampleResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParseMultipleRequestAndResponseTypesResponse parses an HTTP response from a MultipleRequestAndResponseTypesWithResponse call func ParseMultipleRequestAndResponseTypesResponse(rsp *http.Response) (*MultipleRequestAndResponseTypesResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/internal/test/strict-server/echo/server.gen.go b/internal/test/strict-server/echo/server.gen.go index af647a026..8c5205262 100644 --- a/internal/test/strict-server/echo/server.gen.go +++ b/internal/test/strict-server/echo/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -32,6 +33,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(ctx echo.Context) error + // (POST /multipart-related) + MultipartRelatedExample(ctx echo.Context) error + // (POST /multiple) MultipleRequestAndResponseTypes(ctx echo.Context) error @@ -83,6 +87,15 @@ func (w *ServerInterfaceWrapper) MultipartExample(ctx echo.Context) error { return err } +// MultipartRelatedExample converts echo context to params. +func (w *ServerInterfaceWrapper) MultipartRelatedExample(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.MultipartRelatedExample(ctx) + return err +} + // MultipleRequestAndResponseTypes converts echo context to params. func (w *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(ctx echo.Context) error { var err error @@ -238,6 +251,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.POST(baseURL+"/json", wrapper.JSONExample) router.POST(baseURL+"/multipart", wrapper.MultipartExample) + router.POST(baseURL+"/multipart-related", wrapper.MultipartRelatedExample) router.POST(baseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes) router.GET(baseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) router.POST(baseURL+"/reusable-responses", wrapper.ReusableResponses) @@ -331,6 +345,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + w.Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -754,6 +803,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -852,6 +904,37 @@ func (sh *strictHandler) MultipartExample(ctx echo.Context) error { return nil } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(ctx echo.Context) error { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(ctx.Request().Header.Get("Content-Type")); err != nil { + return err + } else if boundary := params["boundary"]; boundary == "" { + return http.ErrMissingBoundary + } else { + request.Body = multipart.NewReader(ctx.Request().Body, boundary) + } + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx.Request().Context(), request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + return validResponse.VisitMultipartRelatedExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx echo.Context) error { var request MultipleRequestAndResponseTypesRequestObject @@ -1144,23 +1227,24 @@ func (sh *strictHandler) UnionExample(ctx echo.Context) error { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/echo/server.go b/internal/test/strict-server/echo/server.go index 83eb50339..fe744e250 100644 --- a/internal/test/strict-server/echo/server.go +++ b/internal/test/strict-server/echo/server.go @@ -41,6 +41,30 @@ func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExa }), nil } +func (s StrictServer) MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) { + return MultipartRelatedExample200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.Body.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + w, err := writer.CreatePart(part.Header) + if err != nil { + return err + } + _, err = io.Copy(w, part) + if err != nil { + return err + } + if err = part.Close(); err != nil { + return err + } + } + }), nil +} + func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) { switch { case request.Body != nil: diff --git a/internal/test/strict-server/echo/types.gen.go b/internal/test/strict-server/echo/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/echo/types.gen.go +++ b/internal/test/strict-server/echo/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/fiber/server.gen.go b/internal/test/strict-server/fiber/server.gen.go index 3e86046b5..81e8c95d0 100644 --- a/internal/test/strict-server/fiber/server.gen.go +++ b/internal/test/strict-server/fiber/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -31,6 +32,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(c *fiber.Ctx) error + // (POST /multipart-related) + MultipartRelatedExample(c *fiber.Ctx) error + // (POST /multiple) MultipleRequestAndResponseTypes(c *fiber.Ctx) error @@ -78,6 +82,12 @@ func (siw *ServerInterfaceWrapper) MultipartExample(c *fiber.Ctx) error { return siw.Handler.MultipartExample(c) } +// MultipartRelatedExample operation middleware +func (siw *ServerInterfaceWrapper) MultipartRelatedExample(c *fiber.Ctx) error { + + return siw.Handler.MultipartRelatedExample(c) +} + // MultipleRequestAndResponseTypes operation middleware func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(c *fiber.Ctx) error { @@ -203,6 +213,8 @@ func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, option router.Post(options.BaseURL+"/multipart", wrapper.MultipartExample) + router.Post(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample) + router.Post(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes) router.Get(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) @@ -304,6 +316,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(ct return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(ctx *fiber.Ctx) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(ctx *fiber.Ctx) error { + writer := multipart.NewWriter(ctx.Response().BodyWriter()) + ctx.Response().Header.Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + ctx.Status(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(ctx *fiber.Ctx) error { + ctx.Status(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(ctx *fiber.Ctx) error { + ctx.Status(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -727,6 +774,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -826,6 +876,39 @@ func (sh *strictHandler) MultipartExample(ctx *fiber.Ctx) error { return nil } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(ctx *fiber.Ctx) error { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(string(ctx.Request().Header.ContentType())); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else if boundary := params["boundary"]; boundary == "" { + return fiber.NewError(fiber.StatusBadRequest, http.ErrMissingBoundary.Error()) + } else { + request.Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), boundary) + } + + handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx.UserContext(), request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + if err := validResponse.VisitMultipartRelatedExampleResponse(ctx); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *fiber.Ctx) error { var request MultipleRequestAndResponseTypesRequestObject @@ -1119,23 +1202,24 @@ func (sh *strictHandler) UnionExample(ctx *fiber.Ctx) error { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/fiber/server.go b/internal/test/strict-server/fiber/server.go index 83eb50339..fe744e250 100644 --- a/internal/test/strict-server/fiber/server.go +++ b/internal/test/strict-server/fiber/server.go @@ -41,6 +41,30 @@ func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExa }), nil } +func (s StrictServer) MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) { + return MultipartRelatedExample200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.Body.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + w, err := writer.CreatePart(part.Header) + if err != nil { + return err + } + _, err = io.Copy(w, part) + if err != nil { + return err + } + if err = part.Close(); err != nil { + return err + } + } + }), nil +} + func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) { switch { case request.Body != nil: diff --git a/internal/test/strict-server/fiber/types.gen.go b/internal/test/strict-server/fiber/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/fiber/types.gen.go +++ b/internal/test/strict-server/fiber/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/gin/server.gen.go b/internal/test/strict-server/gin/server.gen.go index d08ad6004..f11879837 100644 --- a/internal/test/strict-server/gin/server.gen.go +++ b/internal/test/strict-server/gin/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -32,6 +33,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(c *gin.Context) + // (POST /multipart-related) + MultipartRelatedExample(c *gin.Context) + // (POST /multiple) MultipleRequestAndResponseTypes(c *gin.Context) @@ -95,6 +99,19 @@ func (siw *ServerInterfaceWrapper) MultipartExample(c *gin.Context) { siw.Handler.MultipartExample(c) } +// MultipartRelatedExample operation middleware +func (siw *ServerInterfaceWrapper) MultipartRelatedExample(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.MultipartRelatedExample(c) +} + // MultipleRequestAndResponseTypes operation middleware func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(c *gin.Context) { @@ -300,6 +317,7 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options router.POST(options.BaseURL+"/json", wrapper.JSONExample) router.POST(options.BaseURL+"/multipart", wrapper.MultipartExample) + router.POST(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample) router.POST(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes) router.GET(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) router.POST(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) @@ -392,6 +410,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + w.Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -815,6 +868,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -920,6 +976,41 @@ func (sh *strictHandler) MultipartExample(ctx *gin.Context) { } } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(ctx *gin.Context) { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(ctx.Request.Header.Get("Content-Type")); err != nil { + ctx.Error(err) + return + } else if boundary := params["boundary"]; boundary == "" { + ctx.Error(http.ErrMissingBoundary) + return + } else { + request.Body = multipart.NewReader(ctx.Request.Body, boundary) + } + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx, request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + if err := validResponse.VisitMultipartRelatedExampleResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *gin.Context) { var request MultipleRequestAndResponseTypesRequestObject @@ -1244,23 +1335,24 @@ func (sh *strictHandler) UnionExample(ctx *gin.Context) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/gin/server.go b/internal/test/strict-server/gin/server.go index 83eb50339..fe744e250 100644 --- a/internal/test/strict-server/gin/server.go +++ b/internal/test/strict-server/gin/server.go @@ -41,6 +41,30 @@ func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExa }), nil } +func (s StrictServer) MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) { + return MultipartRelatedExample200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.Body.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + w, err := writer.CreatePart(part.Header) + if err != nil { + return err + } + _, err = io.Copy(w, part) + if err != nil { + return err + } + if err = part.Close(); err != nil { + return err + } + } + }), nil +} + func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) { switch { case request.Body != nil: diff --git a/internal/test/strict-server/gin/types.gen.go b/internal/test/strict-server/gin/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/gin/types.gen.go +++ b/internal/test/strict-server/gin/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/gorilla/server.gen.go b/internal/test/strict-server/gorilla/server.gen.go index fcf9c712f..080fa3f93 100644 --- a/internal/test/strict-server/gorilla/server.gen.go +++ b/internal/test/strict-server/gorilla/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -32,6 +33,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(w http.ResponseWriter, r *http.Request) + // (POST /multipart-related) + MultipartRelatedExample(w http.ResponseWriter, r *http.Request) + // (POST /multiple) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) @@ -99,6 +103,21 @@ func (siw *ServerInterfaceWrapper) MultipartExample(w http.ResponseWriter, r *ht handler.ServeHTTP(w, r.WithContext(ctx)) } +// MultipartRelatedExample operation middleware +func (siw *ServerInterfaceWrapper) MultipartRelatedExample(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.MultipartRelatedExample(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + // MultipleRequestAndResponseTypes operation middleware func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -411,6 +430,8 @@ func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.H r.HandleFunc(options.BaseURL+"/multipart", wrapper.MultipartExample).Methods("POST") + r.HandleFunc(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample).Methods("POST") + r.HandleFunc(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes).Methods("POST") r.HandleFunc(options.BaseURL+"/reserved-go-keyword-parameters/{type}", wrapper.ReservedGoKeywordParameters).Methods("GET") @@ -513,6 +534,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + w.Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -936,6 +992,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -1055,6 +1114,40 @@ func (sh *strictHandler) MultipartExample(w http.ResponseWriter, r *http.Request } } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(w http.ResponseWriter, r *http.Request) { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, err) + return + } else if boundary := params["boundary"]; boundary == "" { + sh.options.RequestErrorHandlerFunc(w, r, http.ErrMissingBoundary) + return + } else { + request.Body = multipart.NewReader(r.Body, boundary) + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx, request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + if err := validResponse.VisitMultipartRelatedExampleResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(w http.ResponseWriter, r *http.Request) { var request MultipleRequestAndResponseTypesRequestObject @@ -1366,23 +1459,24 @@ func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/gorilla/types.gen.go b/internal/test/strict-server/gorilla/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/gorilla/types.gen.go +++ b/internal/test/strict-server/gorilla/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/iris/server.gen.go b/internal/test/strict-server/iris/server.gen.go index fe649bc0c..54d6f4982 100644 --- a/internal/test/strict-server/iris/server.gen.go +++ b/internal/test/strict-server/iris/server.gen.go @@ -11,6 +11,7 @@ import ( "encoding/json" "fmt" "io" + "mime" "mime/multipart" "net/http" "net/url" @@ -32,6 +33,9 @@ type ServerInterface interface { // (POST /multipart) MultipartExample(ctx iris.Context) + // (POST /multipart-related) + MultipartRelatedExample(ctx iris.Context) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx iris.Context) @@ -81,6 +85,13 @@ func (w *ServerInterfaceWrapper) MultipartExample(ctx iris.Context) { w.Handler.MultipartExample(ctx) } +// MultipartRelatedExample converts iris context to params. +func (w *ServerInterfaceWrapper) MultipartRelatedExample(ctx iris.Context) { + + // Invoke the callback with all the unmarshaled arguments + w.Handler.MultipartRelatedExample(ctx) +} + // MultipleRequestAndResponseTypes converts iris context to params. func (w *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(ctx iris.Context) { @@ -225,6 +236,7 @@ func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, o router.Post(options.BaseURL+"/json", wrapper.JSONExample) router.Post(options.BaseURL+"/multipart", wrapper.MultipartExample) + router.Post(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample) router.Post(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes) router.Get(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) router.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) @@ -319,6 +331,41 @@ func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(ct return nil } +type MultipartRelatedExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartRelatedExampleResponseObject interface { + VisitMultipartRelatedExampleResponse(ctx iris.Context) error +} + +type MultipartRelatedExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartRelatedExample200MultipartResponse) VisitMultipartRelatedExampleResponse(ctx iris.Context) error { + writer := multipart.NewWriter(ctx.ResponseWriter()) + ctx.ResponseWriter().Header().Set("Content-Type", mime.FormatMediaType("multipart/related", map[string]string{"boundary": writer.Boundary()})) + ctx.StatusCode(200) + + defer writer.Close() + return response(writer) +} + +type MultipartRelatedExample400Response = BadrequestResponse + +func (response MultipartRelatedExample400Response) VisitMultipartRelatedExampleResponse(ctx iris.Context) error { + ctx.StatusCode(400) + return nil +} + +type MultipartRelatedExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartRelatedExampledefaultResponse) VisitMultipartRelatedExampleResponse(ctx iris.Context) error { + ctx.StatusCode(response.StatusCode) + return nil +} + type MultipleRequestAndResponseTypesRequestObject struct { JSONBody *MultipleRequestAndResponseTypesJSONRequestBody FormdataBody *MultipleRequestAndResponseTypesFormdataRequestBody @@ -742,6 +789,9 @@ type StrictServerInterface interface { // (POST /multipart) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) + // (POST /multipart-related) + MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) + // (POST /multiple) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) @@ -850,6 +900,43 @@ func (sh *strictHandler) MultipartExample(ctx iris.Context) { } } +// MultipartRelatedExample operation middleware +func (sh *strictHandler) MultipartRelatedExample(ctx iris.Context) { + var request MultipartRelatedExampleRequestObject + + if _, params, err := mime.ParseMediaType(ctx.Request().Header.Get("Content-Type")); err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } else if boundary := params["boundary"]; boundary == "" { + ctx.StopWithError(http.StatusBadRequest, http.ErrMissingBoundary) + return + } else { + request.Body = multipart.NewReader(ctx.Request().Body, boundary) + } + + handler := func(ctx iris.Context, request interface{}) (interface{}, error) { + return sh.ssi.MultipartRelatedExample(ctx, request.(MultipartRelatedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartRelatedExample") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } else if validResponse, ok := response.(MultipartRelatedExampleResponseObject); ok { + if err := validResponse.VisitMultipartRelatedExampleResponse(ctx); err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } + } else if response != nil { + ctx.Writef("Unexpected response type: %T", response) + return + } +} + // MultipleRequestAndResponseTypes operation middleware func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx iris.Context) { var request MultipleRequestAndResponseTypesRequestObject @@ -1188,23 +1275,24 @@ func (sh *strictHandler) UnionExample(ctx iris.Context) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "e8okwiqPwr8BAAD//4h9qqfAGAAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/test/strict-server/iris/server.go b/internal/test/strict-server/iris/server.go index ea9409551..f35e2f82b 100644 --- a/internal/test/strict-server/iris/server.go +++ b/internal/test/strict-server/iris/server.go @@ -40,6 +40,30 @@ func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExa }), nil } +func (s StrictServer) MultipartRelatedExample(ctx context.Context, request MultipartRelatedExampleRequestObject) (MultipartRelatedExampleResponseObject, error) { + return MultipartRelatedExample200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.Body.NextPart() + if err == io.EOF { + return nil + } else if err != nil { + return err + } + w, err := writer.CreatePart(part.Header) + if err != nil { + return err + } + _, err = io.Copy(w, part) + if err != nil { + return err + } + if err = part.Close(); err != nil { + return err + } + } + }), nil +} + func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) { switch { case request.Body != nil: diff --git a/internal/test/strict-server/iris/types.gen.go b/internal/test/strict-server/iris/types.gen.go index 56eec5c2e..c16705f32 100644 --- a/internal/test/strict-server/iris/types.gen.go +++ b/internal/test/strict-server/iris/types.gen.go @@ -29,6 +29,9 @@ type JSONExampleJSONRequestBody = Example // MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType. type MultipartExampleMultipartRequestBody = Example +// MultipartRelatedExampleMultipartRequestBody defines body for MultipartRelatedExample for multipart/related ContentType. +type MultipartRelatedExampleMultipartRequestBody = Example + // MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType. type MultipleRequestAndResponseTypesJSONRequestBody = Example diff --git a/internal/test/strict-server/strict-schema.yaml b/internal/test/strict-server/strict-schema.yaml index b75759264..a0b6a6e05 100644 --- a/internal/test/strict-server/strict-schema.yaml +++ b/internal/test/strict-server/strict-schema.yaml @@ -64,6 +64,25 @@ paths: $ref: "#/components/responses/badrequest" default: description: Unknown error + /multipart-related: + post: + operationId: MultipartRelatedExample + requestBody: + content: + multipart/related: + schema: + $ref: "#/components/schemas/example" + responses: + 200: + description: OK + content: + multipart/related: + schema: + $ref: "#/components/schemas/example" + 400: + $ref: "#/components/responses/badrequest" + default: + description: Unknown error /text: post: operationId: TextExample diff --git a/internal/test/strict-server/strict_test.go b/internal/test/strict-server/strict_test.go index 32d2d89a7..a1143e89f 100644 --- a/internal/test/strict-server/strict_test.go +++ b/internal/test/strict-server/strict_test.go @@ -122,6 +122,30 @@ func testImpl(t *testing.T, handler http.Handler) { _, err = reader.NextPart() assert.Equal(t, io.EOF, err) }) + t.Run("MultipartRelatedExample", func(t *testing.T) { + value := "789" + fieldName := "value" + var writer bytes.Buffer + mw := multipart.NewWriter(&writer) + field, err := mw.CreateFormField(fieldName) + assert.NoError(t, err) + _, _ = field.Write([]byte(value)) + assert.NoError(t, mw.Close()) + rr := testutil.NewRequest().Post("/multipart-related").WithContentType(mime.FormatMediaType("multipart/related", map[string]string{"boundary": mw.Boundary()})).WithBody(writer.Bytes()).GoWithHTTPHandler(t, handler).Recorder + assert.Equal(t, http.StatusOK, rr.Code) + contentType, params, err := mime.ParseMediaType(rr.Header().Get("Content-Type")) + assert.NoError(t, err) + assert.Equal(t, "multipart/related", contentType) + reader := multipart.NewReader(rr.Body, params["boundary"]) + part, err := reader.NextPart() + assert.NoError(t, err) + assert.Equal(t, part.FormName(), fieldName) + readValue, err := io.ReadAll(part) + assert.NoError(t, err) + assert.Equal(t, value, string(readValue)) + _, err = reader.NextPart() + assert.Equal(t, io.EOF, err) + }) t.Run("TextExample", func(t *testing.T) { value := "text" rr := testutil.NewRequest().Post("/text").WithContentType("text/plain").WithBody([]byte(value)).GoWithHTTPHandler(t, handler).Recorder diff --git a/pkg/codegen/templates/imports.tmpl b/pkg/codegen/templates/imports.tmpl index 6f41a419a..cc1f51587 100644 --- a/pkg/codegen/templates/imports.tmpl +++ b/pkg/codegen/templates/imports.tmpl @@ -15,6 +15,8 @@ import ( "gopkg.in/yaml.v2" "io" "os" + "mime" + "mime/multipart" "net/http" "net/url" "path" diff --git a/pkg/codegen/templates/strict/strict-echo.tmpl b/pkg/codegen/templates/strict/strict-echo.tmpl index 0955fabf7..676690fab 100644 --- a/pkg/codegen/templates/strict/strict-echo.tmpl +++ b/pkg/codegen/templates/strict/strict-echo.tmpl @@ -48,11 +48,21 @@ type strictHandler struct { return err } {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} if reader, err := ctx.Request().MultipartReader(); err != nil { return err } else { request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader } + {{else -}} + if _, params, err := mime.ParseMediaType(ctx.Request().Header.Get("Content-Type")); err != nil { + return err + } else if boundary := params["boundary"]; boundary == "" { + return http.ErrMissingBoundary + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(ctx.Request().Body, boundary) + } + {{end -}} {{else if eq .NameTag "Text" -}} data, err := io.ReadAll(ctx.Request().Body) if err != nil { diff --git a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl index adeda659a..7195b724b 100644 --- a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl @@ -75,7 +75,7 @@ {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(ctx.Response().BodyWriter()) {{end -}} - ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}writer.FormDataContentType(){{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) + ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) {{if not .IsSupported -}} if response.ContentLength != 0 { ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength)) diff --git a/pkg/codegen/templates/strict/strict-fiber.tmpl b/pkg/codegen/templates/strict/strict-fiber.tmpl index a36a8d7ca..510899c9e 100644 --- a/pkg/codegen/templates/strict/strict-fiber.tmpl +++ b/pkg/codegen/templates/strict/strict-fiber.tmpl @@ -46,7 +46,17 @@ type strictHandler struct { } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), string(ctx.Request().Header.MultipartFormBoundary())) + {{else -}} + if _, params, err := mime.ParseMediaType(string(ctx.Request().Header.ContentType())); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else if boundary := params["boundary"]; boundary == "" { + return fiber.NewError(fiber.StatusBadRequest, http.ErrMissingBoundary.Error()) + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), boundary) + } + {{end -}} {{else if eq .NameTag "Text" -}} data := ctx.Request().Body() body := {{$opid}}{{.NameTag}}RequestBody(data) diff --git a/pkg/codegen/templates/strict/strict-gin.tmpl b/pkg/codegen/templates/strict/strict-gin.tmpl index 4a19170c1..ff8eec36b 100644 --- a/pkg/codegen/templates/strict/strict-gin.tmpl +++ b/pkg/codegen/templates/strict/strict-gin.tmpl @@ -51,12 +51,24 @@ type strictHandler struct { } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} if reader, err := ctx.Request.MultipartReader(); err == nil { request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader } else { ctx.Error(err) return } + {{else -}} + if _, params, err := mime.ParseMediaType(ctx.Request.Header.Get("Content-Type")); err != nil { + ctx.Error(err) + return + } else if boundary := params["boundary"]; boundary == "" { + ctx.Error(http.ErrMissingBoundary) + return + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(ctx.Request.Body, boundary) + } + {{end -}} {{else if eq .NameTag "Text" -}} data, err := io.ReadAll(ctx.Request.Body) if err != nil { diff --git a/pkg/codegen/templates/strict/strict-http.tmpl b/pkg/codegen/templates/strict/strict-http.tmpl index 05272af26..e19ea8447 100644 --- a/pkg/codegen/templates/strict/strict-http.tmpl +++ b/pkg/codegen/templates/strict/strict-http.tmpl @@ -67,12 +67,24 @@ type strictHandler struct { } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} if reader, err := r.MultipartReader(); err != nil { sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode multipart body: %w", err)) return } else { request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader } + {{else -}} + if _, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, err) + return + } else if boundary := params["boundary"]; boundary == "" { + sh.options.RequestErrorHandlerFunc(w, r, http.ErrMissingBoundary) + return + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(r.Body, boundary) + } + {{end -}} {{else if eq .NameTag "Text" -}} data, err := io.ReadAll(r.Body) if err != nil { diff --git a/pkg/codegen/templates/strict/strict-interface.tmpl b/pkg/codegen/templates/strict/strict-interface.tmpl index 8c6bcfba4..99b2c854f 100644 --- a/pkg/codegen/templates/strict/strict-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-interface.tmpl @@ -74,7 +74,7 @@ {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(w) {{end -}} - w.Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}writer.FormDataContentType(){{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) + w.Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) {{if not .IsSupported -}} if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) diff --git a/pkg/codegen/templates/strict/strict-iris-interface.tmpl b/pkg/codegen/templates/strict/strict-iris-interface.tmpl index 3a5105601..af0d250e6 100644 --- a/pkg/codegen/templates/strict/strict-iris-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-iris-interface.tmpl @@ -77,7 +77,7 @@ {{if eq .NameTag "Multipart" -}} writer := multipart.NewWriter(ctx.ResponseWriter()) {{end -}} - ctx.ResponseWriter().Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}writer.FormDataContentType(){{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) + ctx.ResponseWriter().Header().Set("Content-Type", {{if eq .NameTag "Multipart"}}{{if eq .ContentType "multipart/form-data"}}writer.FormDataContentType(){{else}}mime.FormatMediaType("{{.ContentType}}", map[string]string{"boundary": writer.Boundary()}){{end}}{{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}}) {{if not .IsSupported -}} if response.ContentLength != 0 { ctx.ResponseWriter().Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) diff --git a/pkg/codegen/templates/strict/strict-iris.tmpl b/pkg/codegen/templates/strict/strict-iris.tmpl index d18bda74c..dfdeb1546 100644 --- a/pkg/codegen/templates/strict/strict-iris.tmpl +++ b/pkg/codegen/templates/strict/strict-iris.tmpl @@ -50,12 +50,24 @@ type strictHandler struct { } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body {{else if eq .NameTag "Multipart" -}} + {{if eq .ContentType "multipart/form-data" -}} if reader, err := ctx.Request().MultipartReader(); err == nil { request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = reader } else { ctx.StopWithError(http.StatusBadRequest, err) return } + {{else -}} + if _, params, err := mime.ParseMediaType(ctx.Request().Header.Get("Content-Type")); err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } else if boundary := params["boundary"]; boundary == "" { + ctx.StopWithError(http.StatusBadRequest, http.ErrMissingBoundary) + return + } else { + request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(ctx.Request().Body, boundary) + } + {{end -}} {{else if eq .NameTag "Text" -}} data, err := io.ReadAll(ctx.Request().Body) if err != nil { From 0c61fb3567c742580525ebbc8bbb708c231fde1e Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Wed, 13 Dec 2023 12:20:43 +0000 Subject: [PATCH 09/25] Support media type parameters for `IsMediaTypeJson` Although not _technically_ a valid extension to JSON according to [the media type definition], it's got a number of services using it for a `v` or `version` parameter, so we should support this as a valid option. This makes sure that we parse it as a valid media type, performing the same checks as we did before by ignoring the parameters. [the media type definition]: https://www.iana.org/assignments/media-types/application/json --- pkg/util/isjson.go | 11 +++++++++-- pkg/util/isjson_test.go | 12 ++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/pkg/util/isjson.go b/pkg/util/isjson.go index 63f63729b..5013ba7a7 100644 --- a/pkg/util/isjson.go +++ b/pkg/util/isjson.go @@ -1,7 +1,14 @@ package util -import "strings" +import ( + "mime" + "strings" +) func IsMediaTypeJson(mediaType string) bool { - return mediaType == "application/json" || strings.HasSuffix(mediaType, "+json") + parsed, _, err := mime.ParseMediaType(mediaType) + if err != nil { + return false + } + return parsed == "application/json" || strings.HasSuffix(parsed, "+json") } diff --git a/pkg/util/isjson_test.go b/pkg/util/isjson_test.go index be3cd2463..8271aef49 100644 --- a/pkg/util/isjson_test.go +++ b/pkg/util/isjson_test.go @@ -41,6 +41,18 @@ func TestIsMediaTypeJson(t *testing.T) { mediaType: "application/vnd.api+json", want: true, }, + { + // NOTE that this _technically_ isn't a standard extension to JSON https://www.iana.org/assignments/media-types/application/json but due to the fact that several APIs do use it, we should support it + name: "When MediaType is application/json;v=1, returns true", + mediaType: "application/json;v=1", + want: true, + }, + { + // NOTE that this _technically_ isn't a standard extension to JSON https://www.iana.org/assignments/media-types/application/json but due to the fact that several APIs do use it, we should support it + name: "When MediaType is application/json;version=1, returns true", + mediaType: "application/json;version=1", + want: true, + }, } for _, test := range suite { t.Run(test.name, func(t *testing.T) { From 839a661de960d4b11725c46c5a300d66642b0bcc Mon Sep 17 00:00:00 2001 From: ShouheiNishi <96609867+ShouheiNishi@users.noreply.github.com> Date: Wed, 13 Dec 2023 22:13:33 +0900 Subject: [PATCH 10/25] Fix: Bind JSON bodies correctly with Gin (#1299) When using the strict server, binding a request body that is JSON compatible - but not `application/json` - will result in errors. We can therefore correctly bind the body using `ShouldBindJSON`. Closes #1298. --- internal/test/issues/issue-1298/config.yaml | 7 + internal/test/issues/issue-1298/doc.go | 3 + .../test/issues/issue-1298/issue1298.gen.go | 400 ++++++++++++++++++ .../test/issues/issue-1298/issue1298.yaml | 25 ++ .../test/issues/issue-1298/issue1298_test.go | 44 ++ internal/test/strict-server/gin/server.gen.go | 10 +- pkg/codegen/templates/strict/strict-gin.tmpl | 2 +- 7 files changed, 485 insertions(+), 6 deletions(-) create mode 100644 internal/test/issues/issue-1298/config.yaml create mode 100644 internal/test/issues/issue-1298/doc.go create mode 100644 internal/test/issues/issue-1298/issue1298.gen.go create mode 100644 internal/test/issues/issue-1298/issue1298.yaml create mode 100644 internal/test/issues/issue-1298/issue1298_test.go diff --git a/internal/test/issues/issue-1298/config.yaml b/internal/test/issues/issue-1298/config.yaml new file mode 100644 index 000000000..a966f3ea4 --- /dev/null +++ b/internal/test/issues/issue-1298/config.yaml @@ -0,0 +1,7 @@ +package: issue1298 +generate: + models: true + client: true + gin-server: true + strict-server: true +output: issue1298.gen.go diff --git a/internal/test/issues/issue-1298/doc.go b/internal/test/issues/issue-1298/doc.go new file mode 100644 index 000000000..dbae518f0 --- /dev/null +++ b/internal/test/issues/issue-1298/doc.go @@ -0,0 +1,3 @@ +package issue1298 + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml issue1298.yaml diff --git a/internal/test/issues/issue-1298/issue1298.gen.go b/internal/test/issues/issue-1298/issue1298.gen.go new file mode 100644 index 000000000..03f8eb392 --- /dev/null +++ b/internal/test/issues/issue-1298/issue1298.gen.go @@ -0,0 +1,400 @@ +// Package issue1298 provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package issue1298 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + "github.com/gin-gonic/gin" + strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" +) + +// Test defines model for Test. +type Test struct { + Field1 string `json:"field1"` + Field2 string `json:"field2"` +} + +// TestApplicationTestPlusJSONRequestBody defines body for Test for application/test+json ContentType. +type TestApplicationTestPlusJSONRequestBody = Test + +// 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. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []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.Client{} + } + 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.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // TestWithBody request with any body + TestWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + TestWithApplicationTestPlusJSONBody(ctx context.Context, body TestApplicationTestPlusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) TestWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTestRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) TestWithApplicationTestPlusJSONBody(ctx context.Context, body TestApplicationTestPlusJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewTestRequestWithApplicationTestPlusJSONBody(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewTestRequestWithApplicationTestPlusJSONBody calls the generic Test builder with application/test+json body +func NewTestRequestWithApplicationTestPlusJSONBody(server string, body TestApplicationTestPlusJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewTestRequestWithBody(server, "application/test+json", bodyReader) +} + +// NewTestRequestWithBody generates requests for Test with any type of body +func NewTestRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/test") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return 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 { + // TestWithBodyWithResponse request with any body + TestWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TestResponse, error) + + TestWithApplicationTestPlusJSONBodyWithResponse(ctx context.Context, body TestApplicationTestPlusJSONRequestBody, reqEditors ...RequestEditorFn) (*TestResponse, error) +} + +type TestResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r TestResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r TestResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// TestWithBodyWithResponse request with arbitrary body returning *TestResponse +func (c *ClientWithResponses) TestWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*TestResponse, error) { + rsp, err := c.TestWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseTestResponse(rsp) +} + +func (c *ClientWithResponses) TestWithApplicationTestPlusJSONBodyWithResponse(ctx context.Context, body TestApplicationTestPlusJSONRequestBody, reqEditors ...RequestEditorFn) (*TestResponse, error) { + rsp, err := c.TestWithApplicationTestPlusJSONBody(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseTestResponse(rsp) +} + +// ParseTestResponse parses an HTTP response from a TestWithResponse call +func ParseTestResponse(rsp *http.Response) (*TestResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &TestResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (GET /test) + Test(c *gin.Context) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +type MiddlewareFunc func(c *gin.Context) + +// Test operation middleware +func (siw *ServerInterfaceWrapper) Test(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.Test(c) +} + +// GinServerOptions provides options for the Gin server. +type GinServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router gin.IRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, GinServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { + errorHandler := options.ErrorHandler + if errorHandler == nil { + errorHandler = func(c *gin.Context, err error, statusCode int) { + c.JSON(statusCode, gin.H{"msg": err.Error()}) + } + } + + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandler: errorHandler, + } + + router.GET(options.BaseURL+"/test", wrapper.Test) +} + +type TestRequestObject struct { + Body *TestApplicationTestPlusJSONRequestBody +} + +type TestResponseObject interface { + VisitTestResponse(w http.ResponseWriter) error +} + +type Test204Response struct { +} + +func (response Test204Response) VisitTestResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + + // (GET /test) + Test(ctx context.Context, request TestRequestObject) (TestResponseObject, error) +} + +type StrictHandlerFunc = strictgin.StrictGinHandlerFunc +type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// Test operation middleware +func (sh *strictHandler) Test(ctx *gin.Context) { + var request TestRequestObject + + var body TestApplicationTestPlusJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.Test(ctx, request.(TestRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "Test") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(TestResponseObject); ok { + if err := validResponse.VisitTestResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} diff --git a/internal/test/issues/issue-1298/issue1298.yaml b/internal/test/issues/issue-1298/issue1298.yaml new file mode 100644 index 000000000..889d0c2b1 --- /dev/null +++ b/internal/test/issues/issue-1298/issue1298.yaml @@ -0,0 +1,25 @@ +openapi: "3.0.1" +components: + schemas: + Test: + type: object + properties: + field1: + type: string + field2: + type: string + required: + - field1 + - field2 +paths: + /test: + get: + operationId: test + requestBody: + content: + application/test+json: + schema: + $ref: "#/components/schemas/Test" + responses: + 204: + description: good diff --git a/internal/test/issues/issue-1298/issue1298_test.go b/internal/test/issues/issue-1298/issue1298_test.go new file mode 100644 index 000000000..62bc46884 --- /dev/null +++ b/internal/test/issues/issue-1298/issue1298_test.go @@ -0,0 +1,44 @@ +package issue1298_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + issue1298 "github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1298" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" +) + +type testStrictServerInterface struct { + t *testing.T +} + +// (GET /test) +func (s *testStrictServerInterface) Test(ctx context.Context, request issue1298.TestRequestObject) (issue1298.TestResponseObject, error) { + assert.Equal(s.t, "test1", request.Body.Field1) + assert.Equal(s.t, "test2", request.Body.Field2) + return issue1298.Test204Response{}, nil +} + +func TestIssue1298(t *testing.T) { + g := gin.Default() + issue1298.RegisterHandlersWithOptions(g, + issue1298.NewStrictHandler(&testStrictServerInterface{t: t}, nil), + issue1298.GinServerOptions{}) + ts := httptest.NewServer(g) + defer ts.Close() + + c, err := issue1298.NewClientWithResponses(ts.URL) + assert.NoError(t, err) + res, err := c.TestWithApplicationTestPlusJSONBodyWithResponse( + context.TODO(), + issue1298.Test{ + Field1: "test1", + Field2: "test2", + }, + ) + assert.NoError(t, err) + assert.Equal(t, http.StatusNoContent, res.StatusCode()) +} diff --git a/internal/test/strict-server/gin/server.gen.go b/internal/test/strict-server/gin/server.gen.go index f11879837..4b58ae0e6 100644 --- a/internal/test/strict-server/gin/server.gen.go +++ b/internal/test/strict-server/gin/server.gen.go @@ -916,7 +916,7 @@ func (sh *strictHandler) JSONExample(ctx *gin.Context) { var request JSONExampleRequestObject var body JSONExampleJSONRequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return @@ -1018,7 +1018,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *gin.Context) { if strings.HasPrefix(ctx.GetHeader("Content-Type"), "application/json") { var body MultipleRequestAndResponseTypesJSONRequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return @@ -1111,7 +1111,7 @@ func (sh *strictHandler) ReusableResponses(ctx *gin.Context) { var request ReusableResponsesRequestObject var body ReusableResponsesJSONRequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return @@ -1271,7 +1271,7 @@ func (sh *strictHandler) HeadersExample(ctx *gin.Context, params HeadersExampleP request.Params = params var body HeadersExampleJSONRequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return @@ -1304,7 +1304,7 @@ func (sh *strictHandler) UnionExample(ctx *gin.Context) { var request UnionExampleRequestObject var body UnionExampleJSONRequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return diff --git a/pkg/codegen/templates/strict/strict-gin.tmpl b/pkg/codegen/templates/strict/strict-gin.tmpl index ff8eec36b..d4c43164a 100644 --- a/pkg/codegen/templates/strict/strict-gin.tmpl +++ b/pkg/codegen/templates/strict/strict-gin.tmpl @@ -33,7 +33,7 @@ type strictHandler struct { {{if $multipleBodies}}if strings.HasPrefix(ctx.GetHeader("Content-Type"), "{{.ContentType}}") { {{end}} {{if .IsJSON }} var body {{$opid}}{{.NameTag}}RequestBody - if err := ctx.ShouldBind(&body); err != nil { + if err := ctx.ShouldBindJSON(&body); err != nil { ctx.Status(http.StatusBadRequest) ctx.Error(err) return From c2de8bfe3c34f03a6a327d7fbc26bd27d4f562ed Mon Sep 17 00:00:00 2001 From: Andrea Cimitan Date: Wed, 22 Nov 2023 10:51:18 +0000 Subject: [PATCH 11/25] Use non-deprecated function names for oapi-codegen/runtime As part of oapi-codegen/runtime v1.1.0, we renamed these for common initialisms and deprecated the existing methods, which means this may show up as a warning in IDEs or tooling. --- .../strict/api/petstore-server.gen.go | 4 +- internal/test/any_of/param/param.gen.go | 8 +- internal/test/components/components.gen.go | 78 +++++++++---------- .../test/issues/issue-1189/issue1189.gen.go | 8 +- internal/test/issues/issue-936/api.gen.go | 24 +++--- .../gen/spec_base/issue.gen.go | 4 +- .../gen/spec_ext/issue.gen.go | 4 +- internal/test/strict-server/chi/server.gen.go | 4 +- .../test/strict-server/gorilla/server.gen.go | 4 +- pkg/codegen/templates/strict/strict-http.tmpl | 4 +- pkg/codegen/templates/union.tmpl | 2 +- 11 files changed, 72 insertions(+), 72 deletions(-) diff --git a/examples/petstore-expanded/strict/api/petstore-server.gen.go b/examples/petstore-expanded/strict/api/petstore-server.gen.go index dce258be7..34b58e69f 100644 --- a/examples/petstore-expanded/strict/api/petstore-server.gen.go +++ b/examples/petstore-expanded/strict/api/petstore-server.gen.go @@ -437,8 +437,8 @@ type StrictServerInterface interface { FindPetByID(ctx context.Context, request FindPetByIDRequestObject) (FindPetByIDResponseObject, error) } -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/internal/test/any_of/param/param.gen.go b/internal/test/any_of/param/param.gen.go index b2e310832..1d6b42212 100644 --- a/internal/test/any_of/param/param.gen.go +++ b/internal/test/any_of/param/param.gen.go @@ -70,7 +70,7 @@ func (t *Test) MergeTest0(v Test0) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -96,7 +96,7 @@ func (t *Test) MergeTest1(v Test1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -132,7 +132,7 @@ func (t *Test2) MergeTest20(v Test20) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -158,7 +158,7 @@ func (t *Test2) MergeTest21(v Test21) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go index 3c3cd985b..3a192870c 100644 --- a/internal/test/components/components.gen.go +++ b/internal/test/components/components.gen.go @@ -847,7 +847,7 @@ func (t *AnyOfObject1) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -873,7 +873,7 @@ func (t *AnyOfObject1) MergeOneOfVariant5(v OneOfVariant5) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -909,7 +909,7 @@ func (t *OneOfObject1) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -935,7 +935,7 @@ func (t *OneOfObject1) MergeOneOfVariant2(v OneOfVariant2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -961,7 +961,7 @@ func (t *OneOfObject1) MergeOneOfVariant3(v OneOfVariant3) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -997,7 +997,7 @@ func (t *OneOfObject10) MergeOneOfObject100(v OneOfObject100) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1023,7 +1023,7 @@ func (t *OneOfObject10) MergeOneOfObject101(v OneOfObject101) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1121,7 +1121,7 @@ func (t *OneOfObject11_AdditionalProperties) MergeOneOfObject110(v OneOfObject11 return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1147,7 +1147,7 @@ func (t *OneOfObject11_AdditionalProperties) MergeOneOfObject111(v OneOfObject11 return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1173,7 +1173,7 @@ func (t *OneOfObject11_AdditionalProperties) MergeOneOfObject112(v OneOfObject11 return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1209,7 +1209,7 @@ func (t *OneOfObject12) MergeOneOfObject120(v OneOfObject120) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1235,7 +1235,7 @@ func (t *OneOfObject12) MergeOneOfObject121(v OneOfObject121) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1261,7 +1261,7 @@ func (t *OneOfObject12) MergeOneOfVariant3(v OneOfVariant3) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1287,7 +1287,7 @@ func (t *OneOfObject12) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1327,7 +1327,7 @@ func (t *OneOfObject13) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1357,7 +1357,7 @@ func (t *OneOfObject13) MergeOneOfVariant6(v OneOfVariant6) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1406,7 +1406,7 @@ func (t *OneOfObject2) MergeOneOfObject20(v OneOfObject20) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1432,7 +1432,7 @@ func (t *OneOfObject2) MergeOneOfObject21(v OneOfObject21) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1458,7 +1458,7 @@ func (t *OneOfObject2) MergeOneOfObject22(v OneOfObject22) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1494,7 +1494,7 @@ func (t *OneOfObject3_Union) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1520,7 +1520,7 @@ func (t *OneOfObject3_Union) MergeOneOfVariant2(v OneOfVariant2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1546,7 +1546,7 @@ func (t *OneOfObject3_Union) MergeOneOfVariant3(v OneOfVariant3) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1582,7 +1582,7 @@ func (t *OneOfObject4) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1608,7 +1608,7 @@ func (t *OneOfObject4) MergeOneOfVariant2(v OneOfVariant2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1634,7 +1634,7 @@ func (t *OneOfObject4) MergeOneOfVariant3(v OneOfVariant3) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1706,7 +1706,7 @@ func (t *OneOfObject5) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1734,7 +1734,7 @@ func (t *OneOfObject5) MergeOneOfVariant5(v OneOfVariant5) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1795,7 +1795,7 @@ func (t *OneOfObject6) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1823,7 +1823,7 @@ func (t *OneOfObject6) MergeOneOfVariant5(v OneOfVariant5) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1884,7 +1884,7 @@ func (t *OneOfObject61) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1912,7 +1912,7 @@ func (t *OneOfObject61) MergeOneOfVariant5(v OneOfVariant5) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -1973,7 +1973,7 @@ func (t *OneOfObject62) MergeOneOfVariant4(v OneOfVariant4) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2001,7 +2001,7 @@ func (t *OneOfObject62) MergeOneOfVariant51(v OneOfVariant51) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2060,7 +2060,7 @@ func (t *OneOfObject7_Item) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2086,7 +2086,7 @@ func (t *OneOfObject7_Item) MergeOneOfVariant2(v OneOfVariant2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2122,7 +2122,7 @@ func (t *OneOfObject8) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2148,7 +2148,7 @@ func (t *OneOfObject8) MergeOneOfVariant2(v OneOfVariant2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2222,7 +2222,7 @@ func (t *OneOfObject9) MergeOneOfVariant1(v OneOfVariant1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -2252,7 +2252,7 @@ func (t *OneOfObject9) MergeOneOfVariant6(v OneOfVariant6) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } diff --git a/internal/test/issues/issue-1189/issue1189.gen.go b/internal/test/issues/issue-1189/issue1189.gen.go index 415ffd015..16ebff95a 100644 --- a/internal/test/issues/issue-1189/issue1189.gen.go +++ b/internal/test/issues/issue-1189/issue1189.gen.go @@ -92,7 +92,7 @@ func (t *Test_FieldA) MergeTestFieldA0(v TestFieldA0) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -118,7 +118,7 @@ func (t *Test_FieldA) MergeTestFieldA1(v TestFieldA1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -154,7 +154,7 @@ func (t *Test_FieldC) MergeTestFieldC0(v TestFieldC0) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -180,7 +180,7 @@ func (t *Test_FieldC) MergeTestFieldC1(v TestFieldC1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } diff --git a/internal/test/issues/issue-936/api.gen.go b/internal/test/issues/issue-936/api.gen.go index 83f47f58a..2e94df1e9 100644 --- a/internal/test/issues/issue-936/api.gen.go +++ b/internal/test/issues/issue-936/api.gen.go @@ -90,7 +90,7 @@ func (t *FilterPredicate) MergeFilterValue(v FilterValue) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -116,7 +116,7 @@ func (t *FilterPredicate) MergeFilterPredicate1(v FilterPredicate1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -142,7 +142,7 @@ func (t *FilterPredicate) MergeFilterPredicateOp(v FilterPredicateOp) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -168,7 +168,7 @@ func (t *FilterPredicate) MergeFilterPredicateRangeOp(v FilterPredicateRangeOp) return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -204,7 +204,7 @@ func (t *FilterPredicateOp_Any) MergeFilterPredicateOpAny0(v FilterPredicateOpAn return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -240,7 +240,7 @@ func (t *FilterPredicateOp_None) MergeFilterPredicate(v FilterPredicate) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -266,7 +266,7 @@ func (t *FilterPredicateOp_None) MergeFilterPredicateOpNone1(v FilterPredicateOp return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -302,7 +302,7 @@ func (t *FilterRangeValue) MergeFilterRangeValue0(v FilterRangeValue0) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -328,7 +328,7 @@ func (t *FilterRangeValue) MergeFilterRangeValue1(v FilterRangeValue1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -364,7 +364,7 @@ func (t *FilterValue) MergeFilterValue0(v FilterValue0) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -390,7 +390,7 @@ func (t *FilterValue) MergeFilterValue1(v FilterValue1) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } @@ -416,7 +416,7 @@ func (t *FilterValue) MergeFilterValue2(v FilterValue2) error { return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go index 1a328012e..b8b7e31ab 100644 --- a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go +++ b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go @@ -259,8 +259,8 @@ type StrictServerInterface interface { PostNoTrouble(ctx context.Context, request PostNoTroubleRequestObject) (PostNoTroubleResponseObject, error) } -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go index 002c88dbf..42eef1a8b 100644 --- a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go +++ b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go @@ -165,8 +165,8 @@ type PascalJSONResponse PascalSchema type StrictServerInterface interface { } -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go index 6a720b699..c0579f98b 100644 --- a/internal/test/strict-server/chi/server.gen.go +++ b/internal/test/strict-server/chi/server.gen.go @@ -1100,8 +1100,8 @@ type StrictServerInterface interface { UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) } -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/internal/test/strict-server/gorilla/server.gen.go b/internal/test/strict-server/gorilla/server.gen.go index 080fa3f93..388da5199 100644 --- a/internal/test/strict-server/gorilla/server.gen.go +++ b/internal/test/strict-server/gorilla/server.gen.go @@ -1023,8 +1023,8 @@ type StrictServerInterface interface { UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) } -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/pkg/codegen/templates/strict/strict-http.tmpl b/pkg/codegen/templates/strict/strict-http.tmpl index e19ea8447..8d32415ad 100644 --- a/pkg/codegen/templates/strict/strict-http.tmpl +++ b/pkg/codegen/templates/strict/strict-http.tmpl @@ -1,5 +1,5 @@ -type StrictHandlerFunc = strictnethttp.StrictHttpHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHttpMiddlewareFunc +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) diff --git a/pkg/codegen/templates/union.tmpl b/pkg/codegen/templates/union.tmpl index 8bb34a256..464fb11c4 100644 --- a/pkg/codegen/templates/union.tmpl +++ b/pkg/codegen/templates/union.tmpl @@ -53,7 +53,7 @@ return err } - merged, err := runtime.JsonMerge(t.union, b) + merged, err := runtime.JSONMerge(t.union, b) t.union = merged return err } From 4ceb1d567792bb19895a9d71393e76f79ab61b2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 4 Nov 2023 08:53:17 +0000 Subject: [PATCH 12/25] Update module github.com/golangci/golangci-lint to v1.55.2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a765d6c29..63e8039e3 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ help: @echo " tidy tidy go mod" $(GOBIN)/golangci-lint: - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.54.0 + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOBIN) v1.55.2 .PHONY: tools tools: $(GOBIN)/golangci-lint From 887a24f7278c96aea2efa52a81ac5ffee7a52bb8 Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Date: Wed, 10 Jan 2024 22:05:15 +0530 Subject: [PATCH 13/25] Add test coverage for existing `GoTypeDef` method (#1423) As part of follow-up changes we're making changes to `GoTypeDef`, so it's handy to have some confidence that the existing behaviour isn't affected by the changes. Co-authored-by: Ashutosh Kumar --- pkg/codegen/schema_test.go | 215 +++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 pkg/codegen/schema_test.go diff --git a/pkg/codegen/schema_test.go b/pkg/codegen/schema_test.go new file mode 100644 index 000000000..7574a0e43 --- /dev/null +++ b/pkg/codegen/schema_test.go @@ -0,0 +1,215 @@ +package codegen + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestProperty_GoTypeDef(t *testing.T) { + type fields struct { + GlobalStateDisableRequiredReadOnlyAsPointer bool + Schema Schema + Required bool + Nullable bool + ReadOnly bool + WriteOnly bool + } + tests := []struct { + name string + fields fields + want string + }{ + { + // When pointer is skipped by setting flag SkipOptionalPointer, the + // flag will never be pointer irrespective of other flags. + name: "Set skip optional pointer type for go type", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: true, + RefType: "", + GoType: "int", + }, + }, + want: "int", + }, + + { + // if the field is optional, it will always be pointer irrespective of other + // flags, given that pointer type is not skipped by setting SkipOptionalPointer + // flag to true + name: "When the field is optional", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + RefType: "", + GoType: "int", + }, + Required: false, + }, + want: "*int", + }, + + { + // if the field(custom-type) is optional, it will NOT be a pointer if + // SkipOptionalPointer flag is set to true + name: "Set skip optional pointer type for ref type", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: true, + RefType: "CustomType", + GoType: "int", + }, + Required: false, + }, + want: "CustomType", + }, + + // For the following test cases, SkipOptionalPointer flag is false. + { + name: "When field is required and not nullable", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: true, + Nullable: false, + }, + want: "int", + }, + + { + name: "When field is required and nullable", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: true, + Nullable: true, + }, + want: "*int", + }, + + { + name: "When field is optional and not nullable", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: false, + Nullable: false, + }, + want: "*int", + }, + + { + name: "When field is optional and nullable", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: false, + Nullable: true, + }, + want: "*int", + }, + + // Following tests cases for non-nullable and required; and skip pointer is not opted + { + name: "When field is readOnly it will always be pointer", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: true, + }, + want: "*int", + }, + + { + name: "When field is readOnly and read only pointer disabled", + fields: fields{ + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: true, + }, + want: "int", + }, + + { + name: "When field is readOnly and optional", + fields: fields{ + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: false, + }, + want: "*int", + }, + { + name: "When field is readOnly and optional and read only pointer disabled", + fields: fields{ + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: false, + }, + want: "*int", + }, + + // When field is write only, it will always be pointer unless pointer is + // skipped by setting SkipOptionalPointer flag + { + name: "When field is write only and read only pointer disabled", + fields: fields{ + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + WriteOnly: true, + }, + want: "*int", + }, + + { + name: "When field is write only and read only pointer enabled", + fields: fields{ + GlobalStateDisableRequiredReadOnlyAsPointer: false, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + WriteOnly: true, + }, + want: "*int", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer = tt.fields.GlobalStateDisableRequiredReadOnlyAsPointer + p := Property{ + Schema: tt.fields.Schema, + Required: tt.fields.Required, + Nullable: tt.fields.Nullable, + ReadOnly: tt.fields.ReadOnly, + WriteOnly: tt.fields.WriteOnly, + } + assert.Equal(t, tt.want, p.GoTypeDef()) + }) + } +} From 66f9bb8d73111908bb20d30bae90e65eb49a6770 Mon Sep 17 00:00:00 2001 From: Ashutosh Kumar Date: Wed, 20 Dec 2023 17:50:49 +0530 Subject: [PATCH 14/25] Allow generating `nullable.Nullable` for nullable properties As part of #1039, we've created a new library `oapi-codegen/nullable`, which allows tracking whether: - a field is not sent - a field is sent with an explicit `null` - a field is sent with an explicit value This introduces an opt-in `output-options` flag, `nullable-type`, which can generate the `nullable.Nullable` types. This is opt-in, as existing code will break due to the signature change, as well as a behaviour change. Closes #1039. Co-authored-by: Ashutosh Kumar --- internal/test/go.mod | 1 + internal/test/go.sum | 2 + .../test/issues/issue-1039/client-config.yaml | 4 + internal/test/issues/issue-1039/client.gen.go | 261 ++++++++++++++++++ .../defaultbehaviour/defaultbehaviour_test.go | 54 ++++ .../issue-1039/defaultbehaviour/types.gen.go | 49 ++++ internal/test/issues/issue-1039/doc.go | 6 + internal/test/issues/issue-1039/issue_test.go | 197 +++++++++++++ .../test/issues/issue-1039/server-config.yaml | 4 + internal/test/issues/issue-1039/server.gen.go | 171 ++++++++++++ internal/test/issues/issue-1039/spec.yaml | 84 ++++++ .../type-config-defaultbehaviour.yaml | 4 + .../test/issues/issue-1039/types-config.yaml | 6 + internal/test/issues/issue-1039/types.gen.go | 51 ++++ pkg/codegen/configuration.go | 1 + pkg/codegen/schema.go | 12 +- pkg/codegen/schema_test.go | 243 +++++++++++++++- 17 files changed, 1147 insertions(+), 3 deletions(-) create mode 100644 internal/test/issues/issue-1039/client-config.yaml create mode 100644 internal/test/issues/issue-1039/client.gen.go create mode 100644 internal/test/issues/issue-1039/defaultbehaviour/defaultbehaviour_test.go create mode 100644 internal/test/issues/issue-1039/defaultbehaviour/types.gen.go create mode 100644 internal/test/issues/issue-1039/doc.go create mode 100644 internal/test/issues/issue-1039/issue_test.go create mode 100644 internal/test/issues/issue-1039/server-config.yaml create mode 100644 internal/test/issues/issue-1039/server.gen.go create mode 100644 internal/test/issues/issue-1039/spec.yaml create mode 100644 internal/test/issues/issue-1039/type-config-defaultbehaviour.yaml create mode 100644 internal/test/issues/issue-1039/types-config.yaml create mode 100644 internal/test/issues/issue-1039/types.gen.go diff --git a/internal/test/go.mod b/internal/test/go.mod index 8e8c37ec3..5f6928e48 100644 --- a/internal/test/go.mod +++ b/internal/test/go.mod @@ -13,6 +13,7 @@ require ( github.com/gorilla/mux v1.8.0 github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 github.com/labstack/echo/v4 v4.11.3 + github.com/oapi-codegen/nullable v1.0.1 github.com/oapi-codegen/runtime v1.1.0 github.com/oapi-codegen/testutil v1.0.0 github.com/stretchr/testify v1.8.4 diff --git a/internal/test/go.sum b/internal/test/go.sum index 450aa4a39..382b67a04 100644 --- a/internal/test/go.sum +++ b/internal/test/go.sum @@ -145,6 +145,8 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/oapi-codegen/nullable v1.0.1 h1:/g+R1Kl1qVYhXlVTg+YT4UnHeYqW+cDh9rfzr+pAV/0= +github.com/oapi-codegen/nullable v1.0.1/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= github.com/oapi-codegen/runtime v1.1.0 h1:rJpoNUawn5XTvekgfkvSZr0RqEnoYpFkyvrzfWeFKWM= github.com/oapi-codegen/runtime v1.1.0/go.mod h1:BeSfBkWWWnAnGdyS+S/GnlbmHKzf8/hwkvelJZDeKA8= github.com/oapi-codegen/testutil v1.0.0 h1:1GI2IiMMLh2vDHr1OkNacaYU/VaApKdcmfgl4aeXAa8= diff --git a/internal/test/issues/issue-1039/client-config.yaml b/internal/test/issues/issue-1039/client-config.yaml new file mode 100644 index 000000000..b2039a310 --- /dev/null +++ b/internal/test/issues/issue-1039/client-config.yaml @@ -0,0 +1,4 @@ +package: issue1039 +generate: + client: true +output: client.gen.go diff --git a/internal/test/issues/issue-1039/client.gen.go b/internal/test/issues/issue-1039/client.gen.go new file mode 100644 index 000000000..cf576c717 --- /dev/null +++ b/internal/test/issues/issue-1039/client.gen.go @@ -0,0 +1,261 @@ +// Package issue1039 provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package issue1039 + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +// 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. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []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.Client{} + } + 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.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // ExamplePatchWithBody request with any body + ExamplePatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + ExamplePatch(ctx context.Context, body ExamplePatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) ExamplePatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExamplePatchRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) ExamplePatch(ctx context.Context, body ExamplePatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewExamplePatchRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewExamplePatchRequest calls the generic ExamplePatch builder with application/json body +func NewExamplePatchRequest(server string, body ExamplePatchJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewExamplePatchRequestWithBody(server, "application/json", bodyReader) +} + +// NewExamplePatchRequestWithBody generates requests for ExamplePatch with any type of body +func NewExamplePatchRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/example") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PATCH", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return 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 { + // ExamplePatchWithBodyWithResponse request with any body + ExamplePatchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExamplePatchResponse, error) + + ExamplePatchWithResponse(ctx context.Context, body ExamplePatchJSONRequestBody, reqEditors ...RequestEditorFn) (*ExamplePatchResponse, error) +} + +type ExamplePatchResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r ExamplePatchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ExamplePatchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ExamplePatchWithBodyWithResponse request with arbitrary body returning *ExamplePatchResponse +func (c *ClientWithResponses) ExamplePatchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ExamplePatchResponse, error) { + rsp, err := c.ExamplePatchWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseExamplePatchResponse(rsp) +} + +func (c *ClientWithResponses) ExamplePatchWithResponse(ctx context.Context, body ExamplePatchJSONRequestBody, reqEditors ...RequestEditorFn) (*ExamplePatchResponse, error) { + rsp, err := c.ExamplePatch(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseExamplePatchResponse(rsp) +} + +// ParseExamplePatchResponse parses an HTTP response from a ExamplePatchWithResponse call +func ParseExamplePatchResponse(rsp *http.Response) (*ExamplePatchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ExamplePatchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} diff --git a/internal/test/issues/issue-1039/defaultbehaviour/defaultbehaviour_test.go b/internal/test/issues/issue-1039/defaultbehaviour/defaultbehaviour_test.go new file mode 100644 index 000000000..d49813ccf --- /dev/null +++ b/internal/test/issues/issue-1039/defaultbehaviour/defaultbehaviour_test.go @@ -0,0 +1,54 @@ +package defaultbehaviour + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func ptr[T any](v T) *T { + return &v +} + +func TestNullableDisabled(t *testing.T) { + // include all fields in patch request + patchReq := PatchRequest{ + ComplexRequiredNullable: &ComplexRequiredNullable{ + Name: ptr("test-name"), + }, + SimpleOptionalNonNullable: ptr(SimpleOptionalNonNullable("bar")), + ComplexOptionalNullable: &ComplexOptionalNullable{ + AliasName: ptr("foo-alias"), + Name: ptr("foo"), + }, + SimpleOptionalNullable: ptr(SimpleOptionalNullable(10)), + SimpleRequiredNullable: ptr(SimpleRequiredNullable(5)), + } + + expected := []byte(`{"complex_optional_nullable":{"alias_name":"foo-alias","name":"foo"},"complex_required_nullable":{"name":"test-name"},"simple_optional_non_nullable":"bar","simple_optional_nullable":10,"simple_required_nullable":5}`) + + actual, err := json.Marshal(patchReq) + require.NoError(t, err) + require.Equal(t, string(expected), string(actual)) + + // omit some fields + patchReq = PatchRequest{ + ComplexRequiredNullable: &ComplexRequiredNullable{ + Name: ptr("test-name"), + }, + // SimpleOptionalNonNullable is omitted + ComplexOptionalNullable: &ComplexOptionalNullable{ + AliasName: ptr("test-alias-name"), + Name: ptr("test-name"), + }, + SimpleOptionalNullable: ptr(SimpleOptionalNullable(10)), + // SimpleRequiredNullable is omitted + } + + expected = []byte(`{"complex_optional_nullable":{"alias_name":"test-alias-name","name":"test-name"},"complex_required_nullable":{"name":"test-name"},"simple_optional_nullable":10,"simple_required_nullable":null}`) + + actual, err = json.Marshal(patchReq) + require.NoError(t, err) + require.Equal(t, string(expected), string(actual)) +} diff --git a/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go b/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go new file mode 100644 index 000000000..e16b43613 --- /dev/null +++ b/internal/test/issues/issue-1039/defaultbehaviour/types.gen.go @@ -0,0 +1,49 @@ +// Package defaultbehaviour provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package defaultbehaviour + +// PatchRequest A request to patch an existing user object. +type PatchRequest struct { + // ComplexOptionalNullable Complex, optional and nullable + ComplexOptionalNullable *ComplexOptionalNullable `json:"complex_optional_nullable"` + + // ComplexRequiredNullable Complex required and nullable + ComplexRequiredNullable *ComplexRequiredNullable `json:"complex_required_nullable"` + + // SimpleOptionalNonNullable Simple optional and non nullable + SimpleOptionalNonNullable *SimpleOptionalNonNullable `json:"simple_optional_non_nullable,omitempty"` + + // SimpleOptionalNullable Simple optional and nullable + SimpleOptionalNullable *SimpleOptionalNullable `json:"simple_optional_nullable"` + + // SimpleRequiredNullable Simple required and nullable + SimpleRequiredNullable *SimpleRequiredNullable `json:"simple_required_nullable"` +} + +// ComplexOptionalNullable Complex, optional and nullable +type ComplexOptionalNullable struct { + // AliasName Optional and nullable + AliasName *string `json:"alias_name"` + + // Name Optional and non nullable + Name *string `json:"name,omitempty"` +} + +// ComplexRequiredNullable Complex required and nullable +type ComplexRequiredNullable struct { + // Name Optional and non nullable + Name *string `json:"name,omitempty"` +} + +// SimpleOptionalNonNullable Simple optional and non nullable +type SimpleOptionalNonNullable = string + +// SimpleOptionalNullable Simple optional and nullable +type SimpleOptionalNullable = int + +// SimpleRequiredNullable Simple required and nullable +type SimpleRequiredNullable = int + +// ExamplePatchJSONRequestBody defines body for ExamplePatch for application/json ContentType. +type ExamplePatchJSONRequestBody = PatchRequest diff --git a/internal/test/issues/issue-1039/doc.go b/internal/test/issues/issue-1039/doc.go new file mode 100644 index 000000000..4cb14c3fa --- /dev/null +++ b/internal/test/issues/issue-1039/doc.go @@ -0,0 +1,6 @@ +package issue1039 + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=types-config.yaml spec.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=type-config-defaultbehaviour.yaml spec.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=client-config.yaml spec.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=server-config.yaml spec.yaml diff --git a/internal/test/issues/issue-1039/issue_test.go b/internal/test/issues/issue-1039/issue_test.go new file mode 100644 index 000000000..1cd390c31 --- /dev/null +++ b/internal/test/issues/issue-1039/issue_test.go @@ -0,0 +1,197 @@ +package issue1039 + +import ( + _ "embed" + "encoding/json" + "testing" + + "github.com/oapi-codegen/nullable" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func ptr[T any](v T) *T { + return &v +} + +func TestNullableTypesMarshal(t *testing.T) { + // include all fields in patch request + patchReq := PatchRequest{ + ComplexRequiredNullable: nullable.NewNullableWithValue(ComplexRequiredNullable{ + Name: ptr("test-name"), + }), + SimpleOptionalNonNullable: ptr(SimpleOptionalNonNullable("bar")), + ComplexOptionalNullable: nullable.NewNullableWithValue(ComplexOptionalNullable{ + AliasName: nullable.NewNullableWithValue("foo-alias"), + Name: ptr("foo"), + }), + SimpleOptionalNullable: nullable.NewNullableWithValue(10), + SimpleRequiredNullable: nullable.NewNullableWithValue(5), + } + + expected := []byte(`{"complex_optional_nullable":{"alias_name":"foo-alias","name":"foo"},"complex_required_nullable":{"name":"test-name"},"simple_optional_non_nullable":"bar","simple_optional_nullable":10,"simple_required_nullable":5}`) + + actual, err := json.Marshal(patchReq) + require.NoError(t, err) + require.Equal(t, string(expected), string(actual)) + + // omit some fields + patchReq = PatchRequest{ + ComplexRequiredNullable: nullable.NewNullableWithValue(ComplexRequiredNullable{ + Name: ptr("test-name"), + }), + // SimpleOptionalNonNullable is omitted + ComplexOptionalNullable: nullable.NewNullableWithValue(ComplexOptionalNullable{ + AliasName: nullable.NewNullableWithValue("test-alias-name"), + Name: ptr("test-name"), + }), + SimpleOptionalNullable: nullable.NewNullableWithValue(10), + // SimpleRequiredNullable is omitted + } + + expected = []byte(`{"complex_optional_nullable":{"alias_name":"test-alias-name","name":"test-name"},"complex_required_nullable":{"name":"test-name"},"simple_optional_nullable":10,"simple_required_nullable":0}`) + + actual, err = json.Marshal(patchReq) + require.NoError(t, err) + require.Equal(t, string(expected), string(actual)) +} + +func TestNullableTypesUnmarshal(t *testing.T) { + type testCase struct { + name string + json []byte + assert func(t *testing.T, obj PatchRequest) + } + tests := []testCase{ + { + name: "when empty json is provided", + json: []byte(`{}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + + // check for nullable fields + assert.Falsef(t, obj.SimpleRequiredNullable.IsSpecified(), "SimpleRequiredNullable field should not be set") + assert.Falsef(t, obj.SimpleRequiredNullable.IsNull(), "SimpleRequiredNullable field should not be null") + + assert.Falsef(t, obj.SimpleOptionalNullable.IsSpecified(), "SimpleOptionalNullable field should not be set") + assert.Falsef(t, obj.SimpleOptionalNullable.IsNull(), "SimpleOptionalNullable field should not be null") + + assert.Falsef(t, obj.ComplexOptionalNullable.IsSpecified(), "ComplexOptionalNullable field should not be set") + assert.Falsef(t, obj.ComplexOptionalNullable.IsNull(), "ComplexOptionalNullable field should not be null") + + assert.Falsef(t, obj.ComplexRequiredNullable.IsSpecified(), "ComplexRequiredNullable field should not be set") + assert.Falsef(t, obj.ComplexRequiredNullable.IsNull(), "ComplexRequiredNullable field should not be null") + + // check for non-nullable field + assert.Nilf(t, obj.SimpleOptionalNonNullable, "SimpleOptionalNonNullable field should be nil") + }, + }, + + { + name: "when only empty complex_optional_nullable is provided", + json: []byte(`{"complex_optional_nullable":{}}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + // check for nullable field + assert.Truef(t, obj.ComplexOptionalNullable.IsSpecified(), "ComplexOptionalNullable field should be set") + assert.Falsef(t, obj.ComplexOptionalNullable.IsNull(), "ComplexOptionalNullable field should not be null") + + // other simple nullable fields should not be set and should not be null + assert.Falsef(t, obj.SimpleRequiredNullable.IsSpecified(), "SimpleRequiredNullable field should not be set") + assert.Falsef(t, obj.SimpleRequiredNullable.IsNull(), "SimpleRequiredNullable field should not be null") + + assert.Falsef(t, obj.SimpleOptionalNullable.IsSpecified(), "SimpleOptionalNullable field should not be set") + assert.Falsef(t, obj.SimpleOptionalNullable.IsNull(), "SimpleOptionalNullable field should not be null") + + // other complex nullable fields should not be set and should not be null + assert.Falsef(t, obj.ComplexRequiredNullable.IsSpecified(), "ComplexRequiredNullable field should not be set") + assert.Falsef(t, obj.ComplexRequiredNullable.IsNull(), "ComplexRequiredNullable field should not be null") + + // other non-nullable field should have its zero value + assert.Nilf(t, obj.SimpleOptionalNonNullable, "SimpleOptionalNonNullable field should be nil") + + }, + }, + + { + name: "when only complex_optional_nullable with its `name` child field is provided", + json: []byte(`{"complex_optional_nullable":{"name":"test-name"}}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + + assert.Truef(t, obj.ComplexOptionalNullable.IsSpecified(), "ComplexOptionalNullable field should be set") + assert.Falsef(t, obj.ComplexOptionalNullable.IsNull(), "ComplexOptionalNullable field should not be null") + + gotComplexObj, err := obj.ComplexOptionalNullable.Get() + require.NoError(t, err) + assert.Equalf(t, "test-name", string(*gotComplexObj.Name), "name should be test-name") + + assert.Falsef(t, gotComplexObj.AliasName.IsSpecified(), "child field `alias name` should not be specified") + assert.Falsef(t, gotComplexObj.AliasName.IsNull(), "child field `alias name` should not be null") + }, + }, + + { + name: "when only complex_optional_nullable child fields `name` and `alias name` are provided with non-zero and null values respectively", + json: []byte(`{"complex_optional_nullable":{"name":"test-name","alias_name":null}}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + + assert.Truef(t, obj.ComplexOptionalNullable.IsSpecified(), "ComplexOptionalNullable field should be set") + assert.Falsef(t, obj.ComplexOptionalNullable.IsNull(), "ComplexOptionalNullable field should not be null") + + gotComplexObj, err := obj.ComplexOptionalNullable.Get() + require.NoError(t, err) + assert.Equalf(t, "test-name", string(*gotComplexObj.Name), "name should be test-name") + + assert.Truef(t, gotComplexObj.AliasName.IsSpecified(), "child field `alias name` should be set") + assert.Truef(t, gotComplexObj.AliasName.IsNull(), "child field `alias name` should be null") + }, + }, + + { + name: "when simple_required_nullable is null ", + json: []byte(`{"simple_required_nullable":null}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + + assert.Truef(t, obj.SimpleRequiredNullable.IsSpecified(), "SimpleRequiredNullable field should be set") + assert.Truef(t, obj.SimpleRequiredNullable.IsNull(), "SimpleRequiredNullable field should be null") + }, + }, + + { + name: "when simple_required_nullable is null and organization has non zero value", + json: []byte(`{"complex_optional_nullable":{"name":"foo","alias_name":"bar"},"simple_required_nullable":null}`), + assert: func(t *testing.T, obj PatchRequest) { + t.Helper() + + assert.Truef(t, obj.SimpleRequiredNullable.IsSpecified(), "SimpleRequiredNullable field should be set") + assert.Truef(t, obj.SimpleRequiredNullable.IsNull(), "SimpleRequiredNullable field should be null") + + assert.Truef(t, obj.ComplexOptionalNullable.IsSpecified(), "ComplexOptionalNullable field should be set") + assert.Falsef(t, obj.ComplexOptionalNullable.IsNull(), "ComplexOptionalNullable field should not be null") + + gotComplexObj, err := obj.ComplexOptionalNullable.Get() + require.NoError(t, err) + assert.Equalf(t, "foo", string(*gotComplexObj.Name), "child field `name` should be foo") + + assert.Truef(t, gotComplexObj.AliasName.IsSpecified(), "child field `alias name` should be set") + assert.Falsef(t, gotComplexObj.AliasName.IsNull(), "child field `alias name` should not be null") + + gotAliasName, err := gotComplexObj.AliasName.Get() + require.NoError(t, err) + assert.Equalf(t, "bar", gotAliasName, "child field `alias name` should be bar") + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var obj PatchRequest + err := json.Unmarshal(tt.json, &obj) + require.NoError(t, err) + + tt.assert(t, obj) + }) + } +} diff --git a/internal/test/issues/issue-1039/server-config.yaml b/internal/test/issues/issue-1039/server-config.yaml new file mode 100644 index 000000000..ce837b254 --- /dev/null +++ b/internal/test/issues/issue-1039/server-config.yaml @@ -0,0 +1,4 @@ +package: issue1039 +generate: + chi-server: true +output: server.gen.go diff --git a/internal/test/issues/issue-1039/server.gen.go b/internal/test/issues/issue-1039/server.gen.go new file mode 100644 index 000000000..578c353fe --- /dev/null +++ b/internal/test/issues/issue-1039/server.gen.go @@ -0,0 +1,171 @@ +// Package issue1039 provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package issue1039 + +import ( + "fmt" + "net/http" + + "github.com/go-chi/chi/v5" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (PATCH /example) + ExamplePatch(w http.ResponseWriter, r *http.Request) +} + +// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. + +type Unimplemented struct{} + +// (PATCH /example) +func (_ Unimplemented) ExamplePatch(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// ExamplePatch operation middleware +func (siw *ServerInterfaceWrapper) ExamplePatch(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ExamplePatch(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{}) +} + +type ChiServerOptions struct { + BaseURL string + BaseRouter chi.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = chi.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.Group(func(r chi.Router) { + r.Patch(options.BaseURL+"/example", wrapper.ExamplePatch) + }) + + return r +} diff --git a/internal/test/issues/issue-1039/spec.yaml b/internal/test/issues/issue-1039/spec.yaml new file mode 100644 index 000000000..821b71066 --- /dev/null +++ b/internal/test/issues/issue-1039/spec.yaml @@ -0,0 +1,84 @@ +openapi: 3.0.1 +info: + version: '0.0.1' + title: example + description: | + Make sure that nullable types are generated properly +paths: + /example: + patch: + operationId: examplePatch + requestBody: + description: The patch body + required: true + content: + application/json: + example: + name: Example Patch + schema: + $ref: "#/components/schemas/PatchRequest" + responses: + '200': + description: "OK" + +components: + schemas: + PatchRequest: + type: object + description: A request to patch an existing user object. + required: + - simple_required_nullable + - complex_required_nullable + properties: + simple_required_nullable: + # required and nullable + $ref: "#/components/schemas/simple_required_nullable" + simple_optional_nullable: + # optional and nullable + $ref: "#/components/schemas/simple_optional_nullable" + simple_optional_non_nullable: + # optional and non-nullable + $ref: "#/components/schemas/simple_optional_non_nullable" + complex_required_nullable: + # required and nullable + $ref: "#/components/schemas/complex_required_nullable" + complex_optional_nullable: + # optional and nullable + $ref: "#/components/schemas/complex_optional_nullable" + additionalProperties: false + + simple_required_nullable: + type: integer + nullable: true + description: Simple required and nullable + + simple_optional_nullable: + type: integer + nullable: true + description: Simple optional and nullable + + simple_optional_non_nullable: + type: string + description: Simple optional and non nullable + + complex_required_nullable: + type: object + nullable: true + description: Complex required and nullable + properties: + name: + description: Optional and non nullable + type: string + + complex_optional_nullable: + type: object + description: Complex, optional and nullable + properties: + alias_name: + description: Optional and nullable + type: string + nullable: true + name: + description: Optional and non nullable + type: string + nullable: true diff --git a/internal/test/issues/issue-1039/type-config-defaultbehaviour.yaml b/internal/test/issues/issue-1039/type-config-defaultbehaviour.yaml new file mode 100644 index 000000000..6b84312fd --- /dev/null +++ b/internal/test/issues/issue-1039/type-config-defaultbehaviour.yaml @@ -0,0 +1,4 @@ +package: defaultbehaviour +generate: + models: true +output: defaultbehaviour/types.gen.go diff --git a/internal/test/issues/issue-1039/types-config.yaml b/internal/test/issues/issue-1039/types-config.yaml new file mode 100644 index 000000000..556fc40cf --- /dev/null +++ b/internal/test/issues/issue-1039/types-config.yaml @@ -0,0 +1,6 @@ +package: issue1039 +generate: + models: true +output-options: + nullable-type: true +output: types.gen.go diff --git a/internal/test/issues/issue-1039/types.gen.go b/internal/test/issues/issue-1039/types.gen.go new file mode 100644 index 000000000..e7665c4cf --- /dev/null +++ b/internal/test/issues/issue-1039/types.gen.go @@ -0,0 +1,51 @@ +// Package issue1039 provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package issue1039 + +import "github.com/oapi-codegen/nullable" + +// PatchRequest A request to patch an existing user object. +type PatchRequest struct { + // ComplexOptionalNullable Complex, optional and nullable + ComplexOptionalNullable nullable.Nullable[ComplexOptionalNullable] `json:"complex_optional_nullable,omitempty"` + + // ComplexRequiredNullable Complex required and nullable + ComplexRequiredNullable nullable.Nullable[ComplexRequiredNullable] `json:"complex_required_nullable"` + + // SimpleOptionalNonNullable Simple optional and non nullable + SimpleOptionalNonNullable *SimpleOptionalNonNullable `json:"simple_optional_non_nullable,omitempty"` + + // SimpleOptionalNullable Simple optional and nullable + SimpleOptionalNullable nullable.Nullable[SimpleOptionalNullable] `json:"simple_optional_nullable,omitempty"` + + // SimpleRequiredNullable Simple required and nullable + SimpleRequiredNullable nullable.Nullable[SimpleRequiredNullable] `json:"simple_required_nullable"` +} + +// ComplexOptionalNullable Complex, optional and nullable +type ComplexOptionalNullable struct { + // AliasName Optional and nullable + AliasName nullable.Nullable[string] `json:"alias_name,omitempty"` + + // Name Optional and non nullable + Name *string `json:"name,omitempty"` +} + +// ComplexRequiredNullable Complex required and nullable +type ComplexRequiredNullable struct { + // Name Optional and non nullable + Name *string `json:"name,omitempty"` +} + +// SimpleOptionalNonNullable Simple optional and non nullable +type SimpleOptionalNonNullable = string + +// SimpleOptionalNullable Simple optional and nullable +type SimpleOptionalNullable = int + +// SimpleRequiredNullable Simple required and nullable +type SimpleRequiredNullable = int + +// ExamplePatchJSONRequestBody defines body for ExamplePatch for application/json ContentType. +type ExamplePatchJSONRequestBody = PatchRequest diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index 9533f166e..36664331d 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -98,6 +98,7 @@ type OutputOptions struct { ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types ClientTypeName string `yaml:"client-type-name,omitempty"` // Override the default generated client type with the value InitialismOverrides bool `yaml:"initialism-overrides,omitempty"` // Whether to use the initialism overrides + NullableType bool `yaml:"nullable-type,omitempty"` // Whether to generate nullable type for nullable fields } // UpdateDefaults sets reasonable default values for unset fields in Configuration diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 7f48f7105..3c03d6ed6 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -94,6 +94,9 @@ func (p Property) GoFieldName() string { func (p Property) GoTypeDef() string { typeDef := p.Schema.TypeDecl() + if globalState.options.OutputOptions.NullableType && p.Nullable { + return "nullable.Nullable[" + typeDef + "]" + } if !p.Schema.SkipOptionalPointer && (!p.Required || p.Nullable || (p.ReadOnly && (!p.Required || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer)) || @@ -681,10 +684,15 @@ func GenFieldsFromProperties(props []Property) []string { field += fmt.Sprintf(" %s %s", goFieldName, p.GoTypeDef()) - omitEmpty := !p.Nullable && - (!p.Required || p.ReadOnly || p.WriteOnly) && + shouldOmitEmpty := (!p.Required || p.ReadOnly || p.WriteOnly) && (!p.Required || !p.ReadOnly || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer) + omitEmpty := !p.Nullable && shouldOmitEmpty + + if p.Nullable && globalState.options.OutputOptions.NullableType { + omitEmpty = shouldOmitEmpty + } + // Support x-omitempty if extOmitEmptyValue, ok := p.Extensions[extPropOmitEmpty]; ok { if extOmitEmpty, err := extParseOmitEmpty(extOmitEmptyValue); err == nil { diff --git a/pkg/codegen/schema_test.go b/pkg/codegen/schema_test.go index 7574a0e43..c4b3f8bd7 100644 --- a/pkg/codegen/schema_test.go +++ b/pkg/codegen/schema_test.go @@ -1,8 +1,9 @@ package codegen import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestProperty_GoTypeDef(t *testing.T) { @@ -213,3 +214,243 @@ func TestProperty_GoTypeDef(t *testing.T) { }) } } + +func TestProperty_GoTypeDef_nullable(t *testing.T) { + type fields struct { + GlobalStateDisableRequiredReadOnlyAsPointer bool + GlobalStateNullableType bool + Schema Schema + Required bool + Nullable bool + ReadOnly bool + WriteOnly bool + } + tests := []struct { + name string + fields fields + want string + }{ + { + // Field not nullable. + // When pointer is skipped by setting flag SkipOptionalPointer, the + // flag will never be pointer irrespective of other flags. + name: "Set skip optional pointer type for go type", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: true, + RefType: "", + GoType: "int", + }, + }, + want: "int", + }, + + { + // Field not nullable. + // if the field is optional, it will always be pointer irrespective of other + // flags, given that pointer type is not skipped by setting SkipOptionalPointer + // flag to true + name: "When the field is optional", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + RefType: "", + GoType: "int", + }, + Required: false, + }, + want: "*int", + }, + + { + // Field not nullable. + // if the field(custom type) is optional, it will NOT be a pointer if + // SkipOptionalPointer flag is set to true + name: "Set skip optional pointer type for ref type", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: true, + RefType: "CustomType", + GoType: "int", + }, + Required: false, + }, + want: "CustomType", + }, + + // Field not nullable. + // For the following test case, SkipOptionalPointer flag is false. + { + name: "When field is required and not nullable", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: true, + Nullable: false, + }, + want: "int", + }, + + { + name: "When field is required and nullable", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: true, + Nullable: true, + }, + want: "nullable.Nullable[int]", + }, + + { + name: "When field is optional and not nullable", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: false, + Nullable: false, + }, + want: "*int", + }, + + { + name: "When field is optional and nullable", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + Required: false, + Nullable: true, + }, + want: "nullable.Nullable[int]", + }, + + { + name: "When field is readOnly, non-nullable and required and skip pointer is not opted", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: true, + }, + want: "*int", + }, + + { + name: "When field is readOnly, required, non-nullable and read only pointer disabled", + fields: fields{ + GlobalStateNullableType: true, + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: true, + }, + want: "int", + }, + + { + name: "When field is readOnly, optional and non nullable", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: false, + }, + want: "*int", + }, + { + name: "When field is readOnly and optional and read only pointer disabled", + fields: fields{ + GlobalStateNullableType: true, + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + ReadOnly: true, + Required: false, + }, + want: "*int", + }, + + { + name: "When field is write only and non nullable", + fields: fields{ + GlobalStateNullableType: true, + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + WriteOnly: true, + }, + want: "*int", + }, + + { + name: "When field is write only and nullable", + fields: fields{ + GlobalStateNullableType: true, + GlobalStateDisableRequiredReadOnlyAsPointer: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + WriteOnly: true, + Nullable: true, + }, + want: "nullable.Nullable[int]", + }, + + { + name: "When field is write only, nullable and read only pointer enabled", + fields: fields{ + GlobalStateNullableType: true, + Schema: Schema{ + SkipOptionalPointer: false, + GoType: "int", + }, + WriteOnly: true, + Nullable: true, + }, + want: "nullable.Nullable[int]", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer = tt.fields.GlobalStateDisableRequiredReadOnlyAsPointer + globalState.options.OutputOptions.NullableType = tt.fields.GlobalStateNullableType + p := Property{ + Schema: tt.fields.Schema, + Required: tt.fields.Required, + Nullable: tt.fields.Nullable, + ReadOnly: tt.fields.ReadOnly, + WriteOnly: tt.fields.WriteOnly, + } + assert.Equal(t, tt.want, p.GoTypeDef()) + }) + } +} From ee101e45633cef35cc33830a46aed9a3e8119999 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Fri, 19 Jan 2024 10:32:20 +0000 Subject: [PATCH 15/25] Document how to use `import-mapping` with URLs As raised in #1426, it isn't immediately clear how you could use the import mapping for a URL, so we should amend `externalref` to make it super clear. Closes #1426. --- .../test/externalref/externalref.cfg.yaml | 1 + internal/test/externalref/externalref.gen.go | 20 +- internal/test/externalref/imports_test.go | 4 + .../test/externalref/petstore/config.yaml | 9 + internal/test/externalref/petstore/doc.go | 3 + .../externalref/petstore/externalref.gen.go | 342 ++++++++++++++++++ internal/test/externalref/petstore/spec.yaml | 1 + internal/test/externalref/spec.yaml | 2 + 8 files changed, 378 insertions(+), 4 deletions(-) create mode 100644 internal/test/externalref/petstore/config.yaml create mode 100644 internal/test/externalref/petstore/doc.go create mode 100644 internal/test/externalref/petstore/externalref.gen.go create mode 100644 internal/test/externalref/petstore/spec.yaml diff --git a/internal/test/externalref/externalref.cfg.yaml b/internal/test/externalref/externalref.cfg.yaml index ceb30d778..d19e6dd13 100644 --- a/internal/test/externalref/externalref.cfg.yaml +++ b/internal/test/externalref/externalref.cfg.yaml @@ -5,6 +5,7 @@ generate: import-mapping: ./packageA/spec.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageA ./packageB/spec.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageB + https://petstore3.swagger.io/api/v3/openapi.json: github.com/deepmap/oapi-codegen/v2/internal/test/externalref/petstore output: externalref.gen.go output-options: skip-prune: true diff --git a/internal/test/externalref/externalref.gen.go b/internal/test/externalref/externalref.gen.go index c8cf6970c..3f132b055 100644 --- a/internal/test/externalref/externalref.gen.go +++ b/internal/test/externalref/externalref.gen.go @@ -14,6 +14,7 @@ import ( externalRef0 "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageA" externalRef1 "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageB" + externalRef2 "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/petstore" "github.com/getkin/kin-openapi/openapi3" ) @@ -22,15 +23,20 @@ type Container struct { ObjectA *externalRef0.ObjectA `json:"object_a,omitempty"` ObjectB *externalRef1.ObjectB `json:"object_b,omitempty"` ObjectC *map[string]interface{} `json:"object_c,omitempty"` + Pet *externalRef2.Pet `json:"pet,omitempty"` } // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/5yPQU7EMAxF72JYRtOR2GXHcAC4AfJkPNSotaPEIKEqd0dJKa1gQcUqtvzf/z8TBB2j", - "Coll8BPk0NOIbXxQMWShVJeYNFIypnbS8ysFe8Y63ya6goebbjXqvly6x6a7h+IW5LwPOW2Q8BfyrSul", - "OFgyf5UWHKm+9hEJPGRLLC//qrbGnHbHlB8fwsuFjVVweNrglt7ILegsn1GWqzZXtqHewME7pcwqdane", - "kQQjg4e7w/FwBAcRra+NSvkMAAD//3vXjDblAQAA", + "H4sIAAAAAAAC/5xUwW7bMAz9lYDbUWgybNjBt7W7L4ftVBQDYzOOBlvSKKZrUOjfB0pN7MRbk+YSKPJ7", + "5HvUk56h9n3wjpxEqJ4h1hvqMS/vUKj1vNN1YB+IxVL+Yhv9pSfsQ0dQfTCw9tyjQAXWyedPYEB2gcpf", + "aokhGXDY0xENvvo2DtAobF0LKR12/OoX1QIGnvpOmaUC1HtdCr3zTtA64qnKQv+Jun7PtIYK3s0Ht/MX", + "q/NvGfdFNb5QVpdRbkeU+hzlgEsGAsk5+JIEkhrcq5vY28/zZHxXmBja3F7cRjnLYuMYX49S81r3Q7qS", + "meRpcWWgGt+2lqaRMhA2XvwP7kp8hfo49XSasz1nHElkxt2A/MMYAjVQCW9JYVFQtrl2Q7FmG8R6p7VI", + "ZuXbzLqZbGgWxbNKJbftoboHfETb4arTvUCuKYqi7xp4+IchwfbYy2uz/o6Fc4mHZIDp99aybt2XWYzn", + "93DueoacXAPa9D8vxxsO962Pg2BBja8lNo3Vc8BuORKjdk+rZfvWrX1ubSWnCgw8EsdykPmCBXIYLFTw", + "8WZxs9DxoGzUX0p/AwAA//++YR/sUAUAAA==", } // GetSwagger returns the content of the embedded swagger specification file @@ -84,6 +90,12 @@ func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { } res[rawPath] = rawFunc } + for rawPath, rawFunc := range externalRef2.PathToRawSpec(path.Join(pathPrefix, "https://petstore3.swagger.io/api/v3/openapi.json")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } return res } diff --git a/internal/test/externalref/imports_test.go b/internal/test/externalref/imports_test.go index d5296d85e..c491edc25 100644 --- a/internal/test/externalref/imports_test.go +++ b/internal/test/externalref/imports_test.go @@ -5,6 +5,7 @@ import ( packageA "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageA" packageB "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageB" + petstore "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/petstore" "github.com/stretchr/testify/require" ) @@ -23,6 +24,9 @@ func TestGetSwagger(t *testing.T) { _, err = packageA.GetSwagger() require.Nil(t, err) + _, err = petstore.GetSwagger() + require.Nil(t, err) + _, err = GetSwagger() require.Nil(t, err) } diff --git a/internal/test/externalref/petstore/config.yaml b/internal/test/externalref/petstore/config.yaml new file mode 100644 index 000000000..fbff1a676 --- /dev/null +++ b/internal/test/externalref/petstore/config.yaml @@ -0,0 +1,9 @@ +package: packagea +generate: + models: true + embedded-spec: true +output-options: + skip-prune: true +import-mapping: + ../packageB/spec.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageB +output: externalref.gen.go diff --git a/internal/test/externalref/petstore/doc.go b/internal/test/externalref/petstore/doc.go new file mode 100644 index 000000000..6d11f4bcf --- /dev/null +++ b/internal/test/externalref/petstore/doc.go @@ -0,0 +1,3 @@ +package packagea + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml diff --git a/internal/test/externalref/petstore/externalref.gen.go b/internal/test/externalref/petstore/externalref.gen.go new file mode 100644 index 000000000..24b2cedb3 --- /dev/null +++ b/internal/test/externalref/petstore/externalref.gen.go @@ -0,0 +1,342 @@ +// Package packagea provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package packagea + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/url" + "path" + "strings" + "time" + + externalRef0 "github.com/deepmap/oapi-codegen/v2/internal/test/externalref/packageB" + "github.com/getkin/kin-openapi/openapi3" +) + +const ( + Api_keyScopes = "api_key.Scopes" + Petstore_authScopes = "petstore_auth.Scopes" +) + +// Defines values for OrderStatus. +const ( + Approved OrderStatus = "approved" + Delivered OrderStatus = "delivered" + Placed OrderStatus = "placed" +) + +// Defines values for PetStatus. +const ( + PetStatusAvailable PetStatus = "available" + PetStatusPending PetStatus = "pending" + PetStatusSold PetStatus = "sold" +) + +// Defines values for FindPetsByStatusParamsStatus. +const ( + FindPetsByStatusParamsStatusAvailable FindPetsByStatusParamsStatus = "available" + FindPetsByStatusParamsStatusPending FindPetsByStatusParamsStatus = "pending" + FindPetsByStatusParamsStatusSold FindPetsByStatusParamsStatus = "sold" +) + +// Address defines model for Address. +type Address struct { + City *string `json:"city,omitempty"` + State *string `json:"state,omitempty"` + Street *string `json:"street,omitempty"` + Zip *string `json:"zip,omitempty"` +} + +// ApiResponse defines model for ApiResponse. +type ApiResponse struct { + Code *int32 `json:"code,omitempty"` + Message *string `json:"message,omitempty"` + Type *string `json:"type,omitempty"` +} + +// Category defines model for Category. +type Category struct { + Id *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` +} + +// Customer defines model for Customer. +type Customer struct { + Address *[]Address `json:"address,omitempty"` + Id *int64 `json:"id,omitempty"` + Username *string `json:"username,omitempty"` +} + +// Order defines model for Order. +type Order struct { + Complete *bool `json:"complete,omitempty"` + Id *int64 `json:"id,omitempty"` + PetId *int64 `json:"petId,omitempty"` + Quantity *int32 `json:"quantity,omitempty"` + ShipDate *time.Time `json:"shipDate,omitempty"` + + // Status Order Status + Status *OrderStatus `json:"status,omitempty"` +} + +// OrderStatus Order Status +type OrderStatus string + +// Pet defines model for Pet. +type Pet struct { + Category *Category `json:"category,omitempty"` + Id *int64 `json:"id,omitempty"` + Name string `json:"name"` + PhotoUrls []string `json:"photoUrls"` + + // Status pet status in the store + Status *PetStatus `json:"status,omitempty"` + Tags *[]Tag `json:"tags,omitempty"` +} + +// PetStatus pet status in the store +type PetStatus string + +// Tag defines model for Tag. +type Tag struct { + Id *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` +} + +// User defines model for User. +type User struct { + Email *string `json:"email,omitempty"` + FirstName *string `json:"firstName,omitempty"` + Id *int64 `json:"id,omitempty"` + LastName *string `json:"lastName,omitempty"` + Password *string `json:"password,omitempty"` + Phone *string `json:"phone,omitempty"` + + // UserStatus User Status + UserStatus *int32 `json:"userStatus,omitempty"` + Username *string `json:"username,omitempty"` +} + +// UserArray defines model for UserArray. +type UserArray = []User + +// FindPetsByStatusParams defines parameters for FindPetsByStatus. +type FindPetsByStatusParams struct { + // Status Status values that need to be considered for filter + Status *FindPetsByStatusParamsStatus `form:"status,omitempty" json:"status,omitempty"` +} + +// FindPetsByStatusParamsStatus defines parameters for FindPetsByStatus. +type FindPetsByStatusParamsStatus string + +// FindPetsByTagsParams defines parameters for FindPetsByTags. +type FindPetsByTagsParams struct { + // Tags Tags to filter by + Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"` +} + +// DeletePetParams defines parameters for DeletePet. +type DeletePetParams struct { + ApiKey *string `json:"api_key,omitempty"` +} + +// UpdatePetWithFormParams defines parameters for UpdatePetWithForm. +type UpdatePetWithFormParams struct { + // Name Name of pet that needs to be updated + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Status Status of pet that needs to be updated + Status *string `form:"status,omitempty" json:"status,omitempty"` +} + +// UploadFileParams defines parameters for UploadFile. +type UploadFileParams struct { + // AdditionalMetadata Additional Metadata + AdditionalMetadata *string `form:"additionalMetadata,omitempty" json:"additionalMetadata,omitempty"` +} + +// CreateUsersWithListInputJSONBody defines parameters for CreateUsersWithListInput. +type CreateUsersWithListInputJSONBody = []User + +// LoginUserParams defines parameters for LoginUser. +type LoginUserParams struct { + // Username The user name for login + Username *string `form:"username,omitempty" json:"username,omitempty"` + + // Password The password for login in clear text + Password *string `form:"password,omitempty" json:"password,omitempty"` +} + +// AddPetJSONRequestBody defines body for AddPet for application/json ContentType. +type AddPetJSONRequestBody = Pet + +// AddPetFormdataRequestBody defines body for AddPet for application/x-www-form-urlencoded ContentType. +type AddPetFormdataRequestBody = Pet + +// UpdatePetJSONRequestBody defines body for UpdatePet for application/json ContentType. +type UpdatePetJSONRequestBody = Pet + +// UpdatePetFormdataRequestBody defines body for UpdatePet for application/x-www-form-urlencoded ContentType. +type UpdatePetFormdataRequestBody = Pet + +// PlaceOrderJSONRequestBody defines body for PlaceOrder for application/json ContentType. +type PlaceOrderJSONRequestBody = Order + +// PlaceOrderFormdataRequestBody defines body for PlaceOrder for application/x-www-form-urlencoded ContentType. +type PlaceOrderFormdataRequestBody = Order + +// CreateUserJSONRequestBody defines body for CreateUser for application/json ContentType. +type CreateUserJSONRequestBody = User + +// CreateUserFormdataRequestBody defines body for CreateUser for application/x-www-form-urlencoded ContentType. +type CreateUserFormdataRequestBody = User + +// CreateUsersWithListInputJSONRequestBody defines body for CreateUsersWithListInput for application/json ContentType. +type CreateUsersWithListInputJSONRequestBody = CreateUsersWithListInputJSONBody + +// UpdateUserJSONRequestBody defines body for UpdateUser for application/json ContentType. +type UpdateUserJSONRequestBody = User + +// UpdateUserFormdataRequestBody defines body for UpdateUser for application/x-www-form-urlencoded ContentType. +type UpdateUserFormdataRequestBody = User + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "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", + "NRcKJSi3rBN77Wcrd4//DgAA///gocX++z0AAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + pathPrefix := path.Dir(pathToFile) + + for rawPath, rawFunc := range externalRef0.PathToRawSpec(path.Join(pathPrefix, "../packageB/spec.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/externalref/petstore/spec.yaml b/internal/test/externalref/petstore/spec.yaml new file mode 100644 index 000000000..2d1dcd41d --- /dev/null +++ b/internal/test/externalref/petstore/spec.yaml @@ -0,0 +1 @@ +{"openapi":"3.0.2","info":{"title":"Swagger Petstore - OpenAPI 3.0","description":"This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)","termsOfService":"http://swagger.io/terms/","contact":{"email":"apiteam@swagger.io"},"license":{"name":"Apache 2.0","url":"http://www.apache.org/licenses/LICENSE-2.0.html"},"version":"1.0.17"},"externalDocs":{"description":"Find out more about Swagger","url":"http://swagger.io"},"servers":[{"url":"/api/v3"}],"tags":[{"name":"pet","description":"Everything about your Pets","externalDocs":{"description":"Find out more","url":"http://swagger.io"}},{"name":"store","description":"Access to Petstore orders","externalDocs":{"description":"Find out more about our store","url":"http://swagger.io"}},{"name":"user","description":"Operations about user"}],"paths":{"/pet":{"put":{"tags":["pet"],"summary":"Update an existing pet","description":"Update an existing pet by Id","operationId":"updatePet","requestBody":{"description":"Update an existent pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"},"405":{"description":"Validation exception"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Add a new pet to the store","description":"Add a new pet to the store","operationId":"addPet","requestBody":{"description":"Create a new pet in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Pet"}}},"required":true},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByStatus":{"get":{"tags":["pet"],"summary":"Finds Pets by status","description":"Multiple status values can be provided with comma separated strings","operationId":"findPetsByStatus","parameters":[{"name":"status","in":"query","description":"Status values that need to be considered for filter","required":false,"explode":true,"schema":{"type":"string","default":"available","enum":["available","pending","sold"]}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid status value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/findByTags":{"get":{"tags":["pet"],"summary":"Finds Pets by tags","description":"Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.","operationId":"findPetsByTags","parameters":[{"name":"tags","in":"query","description":"Tags to filter by","required":false,"explode":true,"schema":{"type":"array","items":{"type":"string"}}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}},"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/Pet"}}}}},"400":{"description":"Invalid tag value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}":{"get":{"tags":["pet"],"summary":"Find pet by ID","description":"Returns a single pet","operationId":"getPetById","parameters":[{"name":"petId","in":"path","description":"ID of pet to return","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Pet not found"}},"security":[{"api_key":[]},{"petstore_auth":["write:pets","read:pets"]}]},"post":{"tags":["pet"],"summary":"Updates a pet in the store with form data","description":"","operationId":"updatePetWithForm","parameters":[{"name":"petId","in":"path","description":"ID of pet that needs to be updated","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"name","in":"query","description":"Name of pet that needs to be updated","schema":{"type":"string"}},{"name":"status","in":"query","description":"Status of pet that needs to be updated","schema":{"type":"string"}}],"responses":{"405":{"description":"Invalid input"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]},"delete":{"tags":["pet"],"summary":"Deletes a pet","description":"","operationId":"deletePet","parameters":[{"name":"api_key","in":"header","description":"","required":false,"schema":{"type":"string"}},{"name":"petId","in":"path","description":"Pet id to delete","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid pet value"}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/pet/{petId}/uploadImage":{"post":{"tags":["pet"],"summary":"uploads an image","description":"","operationId":"uploadFile","parameters":[{"name":"petId","in":"path","description":"ID of pet to update","required":true,"schema":{"type":"integer","format":"int64"}},{"name":"additionalMetadata","in":"query","description":"Additional Metadata","required":false,"schema":{"type":"string"}}],"requestBody":{"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiResponse"}}}}},"security":[{"petstore_auth":["write:pets","read:pets"]}]}},"/store/inventory":{"get":{"tags":["store"],"summary":"Returns pet inventories by status","description":"Returns a map of status codes to quantities","operationId":"getInventory","responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"integer","format":"int32"}}}}}},"security":[{"api_key":[]}]}},"/store/order":{"post":{"tags":["store"],"summary":"Place an order for a pet","description":"Place a new order in the store","operationId":"placeOrder","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Order"}}}},"responses":{"200":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"405":{"description":"Invalid input"}}}},"/store/order/{orderId}":{"get":{"tags":["store"],"summary":"Find purchase order by ID","description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.","operationId":"getOrderById","parameters":[{"name":"orderId","in":"path","description":"ID of order that needs to be fetched","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/Order"}},"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}},"delete":{"tags":["store"],"summary":"Delete purchase order by ID","description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors","operationId":"deleteOrder","parameters":[{"name":"orderId","in":"path","description":"ID of the order that needs to be deleted","required":true,"schema":{"type":"integer","format":"int64"}}],"responses":{"400":{"description":"Invalid ID supplied"},"404":{"description":"Order not found"}}}},"/user":{"post":{"tags":["user"],"summary":"Create user","description":"This can only be done by the logged in user.","operationId":"createUser","requestBody":{"description":"Created user object","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}}}}}}},"/user/createWithList":{"post":{"tags":["user"],"summary":"Creates list of users with given input array","description":"Creates list of users with given input array","operationId":"createUsersWithListInput","requestBody":{"content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}},"responses":{"200":{"description":"Successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"default":{"description":"successful operation"}}}},"/user/login":{"get":{"tags":["user"],"summary":"Logs user into the system","description":"","operationId":"loginUser","parameters":[{"name":"username","in":"query","description":"The user name for login","required":false,"schema":{"type":"string"}},{"name":"password","in":"query","description":"The password for login in clear text","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","headers":{"X-Rate-Limit":{"description":"calls per hour allowed by the user","schema":{"type":"integer","format":"int32"}},"X-Expires-After":{"description":"date in UTC when token expires","schema":{"type":"string","format":"date-time"}}},"content":{"application/xml":{"schema":{"type":"string"}},"application/json":{"schema":{"type":"string"}}}},"400":{"description":"Invalid username/password supplied"}}}},"/user/logout":{"get":{"tags":["user"],"summary":"Logs out current logged in user session","description":"","operationId":"logoutUser","parameters":[],"responses":{"default":{"description":"successful operation"}}}},"/user/{username}":{"get":{"tags":["user"],"summary":"Get user by user name","description":"","operationId":"getUserByName","parameters":[{"name":"username","in":"path","description":"The name that needs to be fetched. Use user1 for testing. ","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"successful operation","content":{"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/json":{"schema":{"$ref":"#/components/schemas/User"}}}},"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}},"put":{"tags":["user"],"summary":"Update user","description":"This can only be done by the logged in user.","operationId":"updateUser","parameters":[{"name":"username","in":"path","description":"name that need to be deleted","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"Update an existent user in the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}},"application/xml":{"schema":{"$ref":"#/components/schemas/User"}},"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"default":{"description":"successful operation"}}},"delete":{"tags":["user"],"summary":"Delete user","description":"This can only be done by the logged in user.","operationId":"deleteUser","parameters":[{"name":"username","in":"path","description":"The name that needs to be deleted","required":true,"schema":{"type":"string"}}],"responses":{"400":{"description":"Invalid username supplied"},"404":{"description":"User not found"}}}}},"components":{"schemas":{"Order":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"petId":{"type":"integer","format":"int64","example":198772},"quantity":{"type":"integer","format":"int32","example":7},"shipDate":{"type":"string","format":"date-time"},"status":{"type":"string","description":"Order Status","example":"approved","enum":["placed","approved","delivered"]},"complete":{"type":"boolean"}},"xml":{"name":"order"}},"Customer":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":100000},"username":{"type":"string","example":"fehguy"},"address":{"type":"array","xml":{"name":"addresses","wrapped":true},"items":{"$ref":"#/components/schemas/Address"}}},"xml":{"name":"customer"}},"Address":{"type":"object","properties":{"street":{"type":"string","example":"437 Lytton"},"city":{"type":"string","example":"Palo Alto"},"state":{"type":"string","example":"CA"},"zip":{"type":"string","example":"94301"}},"xml":{"name":"address"}},"Category":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":1},"name":{"type":"string","example":"Dogs"}},"xml":{"name":"category"}},"User":{"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"username":{"type":"string","example":"theUser"},"firstName":{"type":"string","example":"John"},"lastName":{"type":"string","example":"James"},"email":{"type":"string","example":"john@email.com"},"password":{"type":"string","example":"12345"},"phone":{"type":"string","example":"12345"},"userStatus":{"type":"integer","description":"User Status","format":"int32","example":1}},"xml":{"name":"user"}},"Tag":{"type":"object","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}},"xml":{"name":"tag"}},"Pet":{"required":["name","photoUrls"],"type":"object","properties":{"id":{"type":"integer","format":"int64","example":10},"name":{"type":"string","example":"doggie"},"category":{"$ref":"#/components/schemas/Category"},"photoUrls":{"type":"array","xml":{"wrapped":true},"items":{"type":"string","xml":{"name":"photoUrl"}}},"tags":{"type":"array","xml":{"wrapped":true},"items":{"$ref":"#/components/schemas/Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}},"xml":{"name":"pet"}},"ApiResponse":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"type":{"type":"string"},"message":{"type":"string"}},"xml":{"name":"##default"}}},"requestBodies":{"Pet":{"description":"Pet object that needs to be added to the store","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Pet"}},"application/xml":{"schema":{"$ref":"#/components/schemas/Pet"}}}},"UserArray":{"description":"List of user object","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/User"}}}}}},"securitySchemes":{"petstore_auth":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://petstore3.swagger.io/oauth/authorize","scopes":{"write:pets":"modify pets in your account","read:pets":"read your pets"}}}},"api_key":{"type":"apiKey","name":"api_key","in":"header"}}}} \ No newline at end of file diff --git a/internal/test/externalref/spec.yaml b/internal/test/externalref/spec.yaml index ba0ae6555..8f924b75c 100644 --- a/internal/test/externalref/spec.yaml +++ b/internal/test/externalref/spec.yaml @@ -11,3 +11,5 @@ components: $ref: ./packageB/spec.yaml#/components/schemas/ObjectB object_c: $ref: ./object_c.json + pet: + $ref: https://petstore3.swagger.io/api/v3/openapi.json#/components/schemas/Pet From d326c01d279aa0c6cff8c1cd1971fccacdd2409e Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Wed, 20 Dec 2023 12:16:59 +0200 Subject: [PATCH 16/25] refactor(cmd): Use os.Exit(1) only from main() --- cmd/oapi-codegen/oapi-codegen.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go index 95ce915ca..9512fcf67 100644 --- a/cmd/oapi-codegen/oapi-codegen.go +++ b/cmd/oapi-codegen/oapi-codegen.go @@ -240,7 +240,13 @@ func main() { errExit("error parsing'%s' as YAML: %v\n", flagConfigFile, err) } } - opts = newConfigFromOldConfig(oldConfig) + var err error + opts, err = newConfigFromOldConfig(oldConfig) + if err != nil { + flag.PrintDefaults() + errExit("error creating new config from old config: %v\n", err) + } + } // Ensure default values are set if user hasn't specified some needed @@ -488,7 +494,7 @@ func generationTargets(cfg *codegen.Configuration, targets []string) error { return nil } -func newConfigFromOldConfig(c oldConfiguration) configuration { +func newConfigFromOldConfig(c oldConfiguration) (configuration, error) { // Take flags into account. cfg := updateOldConfigFromFlags(c) @@ -500,9 +506,7 @@ func newConfigFromOldConfig(c oldConfiguration) configuration { opts.OutputOptions.ResponseTypeSuffix = flagResponseTypeSuffix if err := generationTargets(&opts, cfg.GenerateTargets); err != nil { - fmt.Println(err) - flag.PrintDefaults() - os.Exit(1) + return configuration{}, fmt.Errorf("generation targets: %w", err) } opts.OutputOptions.IncludeTags = cfg.IncludeTags @@ -511,7 +515,7 @@ func newConfigFromOldConfig(c oldConfiguration) configuration { templates, err := loadTemplateOverrides(cfg.TemplatesDir) if err != nil { - errExit("error loading template overrides: %s\n", err) + return configuration{}, fmt.Errorf("loading template overrides: %w", err) } opts.OutputOptions.UserTemplates = templates @@ -522,5 +526,5 @@ func newConfigFromOldConfig(c oldConfiguration) configuration { return configuration{ Configuration: opts, OutputFile: cfg.OutputFile, - } + }, nil } From f0a1be03dd6dc1f16e5a75037444793ffcca67e6 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 18 Dec 2023 11:36:39 +0000 Subject: [PATCH 17/25] Fix: Refer to external refs correctly in strict interfaces The current approach doesn't often work when generating types that are in external packages, unless that package is also a strict server's definition, which isn't always the case. --- .../issue-removed-external-ref/gen/spec_base/issue.gen.go | 4 +--- pkg/codegen/templates/strict/strict-fiber-interface.tmpl | 2 ++ pkg/codegen/templates/strict/strict-interface.tmpl | 2 ++ pkg/codegen/templates/strict/strict-iris-interface.tmpl | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go index b8b7e31ab..6b2de3157 100644 --- a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go +++ b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go @@ -217,9 +217,7 @@ type PostInvalidExtRefTroubleResponseObject interface { VisitPostInvalidExtRefTroubleResponse(w http.ResponseWriter) error } -type PostInvalidExtRefTrouble300JSONResponse struct { - externalRef0.PascalJSONResponse -} +type PostInvalidExtRefTrouble300JSONResponse struct{ externalRef0.Pascal } func (response PostInvalidExtRefTrouble300JSONResponse) VisitPostInvalidExtRefTroubleResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") diff --git a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl index 7195b724b..ffa98c3c8 100644 --- a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl @@ -42,6 +42,8 @@ {{if and $fixedStatusCode $isRef -}} {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response + {{else if $isExternalRef -}} + type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} diff --git a/pkg/codegen/templates/strict/strict-interface.tmpl b/pkg/codegen/templates/strict/strict-interface.tmpl index 99b2c854f..a719e249d 100644 --- a/pkg/codegen/templates/strict/strict-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-interface.tmpl @@ -44,6 +44,8 @@ {{else if and $fixedStatusCode $isRef -}} {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response + {{else if $isExternalRef -}} + type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} diff --git a/pkg/codegen/templates/strict/strict-iris-interface.tmpl b/pkg/codegen/templates/strict/strict-iris-interface.tmpl index af0d250e6..33f9d9575 100644 --- a/pkg/codegen/templates/strict/strict-iris-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-iris-interface.tmpl @@ -44,6 +44,8 @@ {{else if and $fixedStatusCode $isRef -}} {{ if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) (eq .NameTag "Multipart") -}} type {{$receiverTypeName}} {{$ref}}{{.NameTagOrContentType}}Response + {{else if $isExternalRef -}} + type {{$receiverTypeName}} struct { {{$ref}} } {{else -}} type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} From b5a3855135cfa6e22a5c4902b49aacec9395977d Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 18 Dec 2023 12:20:52 +0000 Subject: [PATCH 18/25] Add a `Schema#IsExternalRef` function As part of follow-up changes, we'll need it, and it's handy to keep this consistent with other types that have this. --- pkg/codegen/schema.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 3c03d6ed6..87d5a2501 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -43,6 +43,13 @@ func (s Schema) IsRef() bool { return s.RefType != "" } +func (s Schema) IsExternalRef() bool { + if !s.IsRef() { + return false + } + return strings.Contains(s.RefType, ".") +} + func (s Schema) TypeDecl() string { if s.IsRef() { return s.RefType From 390058d57578f916c415806bbf52335f4095c01f Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Mon, 18 Dec 2023 12:02:27 +0000 Subject: [PATCH 19/25] Fix: Don't use type aliases with external references In the case that we're referencing a type defined in an external package, using an alias here results in build errors i.e.: ./api.gen.go:93:7: cannot define new methods on non-local type common.TypeNameHere Instead, we can make sure we only alias when there's not an external reference in use, using the new `Schema.IsExternalRef` function. --- pkg/codegen/templates/strict/strict-fiber-interface.tmpl | 2 +- pkg/codegen/templates/strict/strict-interface.tmpl | 2 +- pkg/codegen/templates/strict/strict-iris-interface.tmpl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl index ffa98c3c8..8bcfc89b1 100644 --- a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl @@ -48,7 +48,7 @@ type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} - type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} diff --git a/pkg/codegen/templates/strict/strict-interface.tmpl b/pkg/codegen/templates/strict/strict-interface.tmpl index a719e249d..b26edd467 100644 --- a/pkg/codegen/templates/strict/strict-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-interface.tmpl @@ -50,7 +50,7 @@ type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} - type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} diff --git a/pkg/codegen/templates/strict/strict-iris-interface.tmpl b/pkg/codegen/templates/strict/strict-iris-interface.tmpl index 33f9d9575..e93fdf616 100644 --- a/pkg/codegen/templates/strict/strict-iris-interface.tmpl +++ b/pkg/codegen/templates/strict/strict-iris-interface.tmpl @@ -50,7 +50,7 @@ type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response } {{end}} {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}} - type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} + type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if and .Schema.IsRef (not .Schema.IsExternalRef)}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}} {{else -}} type {{$receiverTypeName}} struct { Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}} From d1b45047032737de0e68cd49c81465cf998b9311 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Thu, 25 Jan 2024 12:36:14 +0100 Subject: [PATCH 20/25] feat: filter by operation ids (#929) --- README.md | 12 +-- cmd/oapi-codegen/oapi-codegen.go | 47 +++++++----- internal/test/filter/doc.go | 4 + internal/test/filter/operations/server.gen.go | 74 +++++++++++++++++++ .../test/filter/operations/server_test.go | 26 +++++++ internal/test/filter/server.yaml | 48 ++++++++++++ internal/test/filter/tags/server.gen.go | 74 +++++++++++++++++++ internal/test/filter/tags/server_test.go | 26 +++++++ pkg/codegen/codegen.go | 1 + pkg/codegen/configuration.go | 12 +-- pkg/codegen/filter.go | 62 +++++++++++++--- pkg/codegen/filter_test.go | 61 +++++++++++++++ 12 files changed, 407 insertions(+), 40 deletions(-) create mode 100644 internal/test/filter/doc.go create mode 100644 internal/test/filter/operations/server.gen.go create mode 100644 internal/test/filter/operations/server_test.go create mode 100644 internal/test/filter/server.yaml create mode 100644 internal/test/filter/tags/server.gen.go create mode 100644 internal/test/filter/tags/server_test.go diff --git a/README.md b/README.md index 14bc53873..c67d92011 100644 --- a/README.md +++ b/README.md @@ -813,13 +813,13 @@ So, for example, if you would like to produce only the server code, you could run `oapi-codegen -generate types,server`. You could generate `types` and `server` into separate files, but both are required for the server code. -`oapi-codegen` can filter paths base on their tags in the openapi definition. -Use either `-include-tags` or `-exclude-tags` followed by a comma-separated list -of tags. For instance, to generate a server that serves all paths except those -tagged with `auth` or `admin`, use the argument, `-exclude-tags="auth,admin"`. +`oapi-codegen` can filter paths base on their tags or operationId in the openapi definition. +Use either `-include-tags`, `include-operation-ids` or `-exclude-tags`, `-exclude-operation-ids` +followed by a comma-separated list of tags or operation ids. For instance, to generate a server +that serves all paths except those tagged with `auth` or `admin`, use the argument, `-exclude-tags="auth,admin"`. 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. +`-include-tags="admin"` or use `-include-operation-ids="getPets"` to include this specific operation. +When neither of these arguments is present, all paths are generated. `oapi-codegen` can filter schemas based on the option `--exclude-schemas`, which is a comma separated list of schema names. For instance, `--exclude-schemas=Pet,NewPet` diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go index 9512fcf67..0c8acf2dc 100644 --- a/cmd/oapi-codegen/oapi-codegen.go +++ b/cmd/oapi-codegen/oapi-codegen.go @@ -47,13 +47,15 @@ var ( // Deprecated: The options below will be removed in a future // release. Please use the new config file format. - flagIncludeTags string - flagExcludeTags string - flagImportMapping string - flagExcludeSchemas string - flagResponseTypeSuffix string - flagAliasTypes bool - flagInitalismOverrides bool + flagIncludeTags string + flagExcludeTags string + flagIncludeOperationIDs string + flagExcludeOperationIDs string + flagImportMapping string + flagExcludeSchemas string + flagResponseTypeSuffix string + flagAliasTypes bool + flagInitalismOverrides bool ) type configuration struct { @@ -66,16 +68,18 @@ type configuration struct { // oldConfiguration is deprecated. Please add no more flags here. It is here // for backwards compatibility, and it will be removed in the future. type oldConfiguration struct { - PackageName string `yaml:"package"` - GenerateTargets []string `yaml:"generate"` - OutputFile string `yaml:"output"` - IncludeTags []string `yaml:"include-tags"` - ExcludeTags []string `yaml:"exclude-tags"` - TemplatesDir string `yaml:"templates"` - ImportMapping map[string]string `yaml:"import-mapping"` - ExcludeSchemas []string `yaml:"exclude-schemas"` - ResponseTypeSuffix string `yaml:"response-type-suffix"` - Compatibility codegen.CompatibilityOptions `yaml:"compatibility"` + PackageName string `yaml:"package"` + GenerateTargets []string `yaml:"generate"` + OutputFile string `yaml:"output"` + IncludeTags []string `yaml:"include-tags"` + ExcludeTags []string `yaml:"exclude-tags"` + IncludeOperationIDs []string `yaml:"include-operation-ids"` + ExcludeOperationIDs []string `yaml:"exclude-operation-ids"` + TemplatesDir string `yaml:"templates"` + ImportMapping map[string]string `yaml:"import-mapping"` + ExcludeSchemas []string `yaml:"exclude-schemas"` + ResponseTypeSuffix string `yaml:"response-type-suffix"` + Compatibility codegen.CompatibilityOptions `yaml:"compatibility"` } // noVCSVersionOverride allows overriding the version of the application for cases where no Version Control System (VCS) is available when building, for instance when using a Nix derivation. @@ -98,6 +102,8 @@ func main() { `Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber", "iris".`) flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.") flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.") + flag.StringVar(&flagIncludeOperationIDs, "include-operation-ids", "", "Only include operations with the given operation-ids. Comma-separated list of operation-ids.") + flag.StringVar(&flagExcludeOperationIDs, "exclude-operation-ids", "", "Exclude operations with the given operation-ids. Comma-separated list of operation-ids.") flag.StringVar(&flagTemplatesDir, "templates", "", "Path to directory containing user templates.") flag.StringVar(&flagImportMapping, "import-mapping", "", "A dict from the external reference to golang package path.") flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation.") @@ -388,6 +394,13 @@ func updateConfigFromFlags(cfg *configuration) error { if flagExcludeTags != "" { cfg.OutputOptions.ExcludeTags = util.ParseCommandLineList(flagExcludeTags) } + if flagIncludeOperationIDs != "" { + cfg.OutputOptions.IncludeOperationIDs = util.ParseCommandLineList(flagIncludeOperationIDs) + } + if flagExcludeOperationIDs != "" { + cfg.OutputOptions.ExcludeOperationIDs = util.ParseCommandLineList(flagExcludeOperationIDs) + } + if flagTemplatesDir != "" { templates, err := loadTemplateOverrides(flagTemplatesDir) if err != nil { diff --git a/internal/test/filter/doc.go b/internal/test/filter/doc.go new file mode 100644 index 000000000..9b33988d8 --- /dev/null +++ b/internal/test/filter/doc.go @@ -0,0 +1,4 @@ +package client + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --package=filtertags --generate=server -o tags/server.gen.go -include-tags included-tag1,included-tag2 server.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --package=filteroperations --generate=server -o operations/server.gen.go -include-operation-ids included-operation1,included-operation2 server.yaml diff --git a/internal/test/filter/operations/server.gen.go b/internal/test/filter/operations/server.gen.go new file mode 100644 index 000000000..a4db89e6d --- /dev/null +++ b/internal/test/filter/operations/server.gen.go @@ -0,0 +1,74 @@ +// Package filteroperations provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package filteroperations + +import ( + "github.com/labstack/echo/v4" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (GET /included1) + IncludedOperation1(ctx echo.Context) error + + // (GET /included2) + IncludedOperation2(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// IncludedOperation1 converts echo context to params. +func (w *ServerInterfaceWrapper) IncludedOperation1(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.IncludedOperation1(ctx) + return err +} + +// IncludedOperation2 converts echo context to params. +func (w *ServerInterfaceWrapper) IncludedOperation2(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.IncludedOperation2(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) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/included1", wrapper.IncludedOperation1) + router.GET(baseURL+"/included2", wrapper.IncludedOperation2) + +} diff --git a/internal/test/filter/operations/server_test.go b/internal/test/filter/operations/server_test.go new file mode 100644 index 000000000..2dc33642b --- /dev/null +++ b/internal/test/filter/operations/server_test.go @@ -0,0 +1,26 @@ +package filteroperations + +import ( + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" +) + +type server struct{} + +func (s server) IncludedOperation1(ctx echo.Context) error { + return nil +} + +func (s server) IncludedOperation2(ctx echo.Context) error { + return nil +} + +func TestServer(t *testing.T) { + assert := assert.New(t) + + var s ServerInterface = server{} + assert.NoError(s.IncludedOperation1(nil)) + assert.NoError(s.IncludedOperation2(nil)) +} diff --git a/internal/test/filter/server.yaml b/internal/test/filter/server.yaml new file mode 100644 index 000000000..63e0a536a --- /dev/null +++ b/internal/test/filter/server.yaml @@ -0,0 +1,48 @@ +openapi: "3.0.1" +info: + version: 1.0.0 + title: Test Server + license: + name: MIT + description: | + This tests whether filtering works correctly +paths: + /included1: + get: + operationId: included-operation1 + tags: + - included-tag1 + responses: + 200: + application/json: + schema: + $ref: '#/components/schemas/SchemaObject' + /filtered: + get: + operationId: filtered-operation + tags: + - filtered-tag + responses: + 200: + application/json: + schema: + $ref: '#/components/schemas/SchemaObject' + /included2: + get: + operationId: included-operation2 + tags: + - included-tag2 + responses: + 200: + application/json: + schema: + $ref: '#/components/schemas/SchemaObject' + +components: + schemas: + SchemaObject: + properties: + firstName: + type: string + required: + - firstName diff --git a/internal/test/filter/tags/server.gen.go b/internal/test/filter/tags/server.gen.go new file mode 100644 index 000000000..f7c993aaf --- /dev/null +++ b/internal/test/filter/tags/server.gen.go @@ -0,0 +1,74 @@ +// Package filtertags provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package filtertags + +import ( + "github.com/labstack/echo/v4" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (GET /included1) + IncludedOperation1(ctx echo.Context) error + + // (GET /included2) + IncludedOperation2(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// IncludedOperation1 converts echo context to params. +func (w *ServerInterfaceWrapper) IncludedOperation1(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.IncludedOperation1(ctx) + return err +} + +// IncludedOperation2 converts echo context to params. +func (w *ServerInterfaceWrapper) IncludedOperation2(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.IncludedOperation2(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) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/included1", wrapper.IncludedOperation1) + router.GET(baseURL+"/included2", wrapper.IncludedOperation2) + +} diff --git a/internal/test/filter/tags/server_test.go b/internal/test/filter/tags/server_test.go new file mode 100644 index 000000000..1f466269c --- /dev/null +++ b/internal/test/filter/tags/server_test.go @@ -0,0 +1,26 @@ +package filtertags + +import ( + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" +) + +type server struct{} + +func (s server) IncludedOperation1(ctx echo.Context) error { + return nil +} + +func (s server) IncludedOperation2(ctx echo.Context) error { + return nil +} + +func TestServer(t *testing.T) { + assert := assert.New(t) + + var s ServerInterface = server{} + assert.NoError(s.IncludedOperation1(nil)) + assert.NoError(s.IncludedOperation2(nil)) +} diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go index f4942eae0..0471d5b7d 100644 --- a/pkg/codegen/codegen.go +++ b/pkg/codegen/codegen.go @@ -110,6 +110,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) { globalState.importMapping = constructImportMapping(opts.ImportMapping) filterOperationsByTag(spec, opts) + filterOperationsByOperationID(spec, opts) if !opts.OutputOptions.SkipPrune { pruneUnusedComponents(spec) } diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index 36664331d..efacfb7c7 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -88,11 +88,13 @@ type CompatibilityOptions struct { // OutputOptions are used to modify the output code in some way. type OutputOptions struct { - SkipFmt bool `yaml:"skip-fmt,omitempty"` // Whether to skip go imports on the generated code - SkipPrune bool `yaml:"skip-prune,omitempty"` // Whether to skip pruning unused components on the generated code - IncludeTags []string `yaml:"include-tags,omitempty"` // Only include operations that have one of these tags. Ignored when empty. - ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty. - UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files + SkipFmt bool `yaml:"skip-fmt,omitempty"` // Whether to skip go imports on the generated code + SkipPrune bool `yaml:"skip-prune,omitempty"` // Whether to skip pruning unused components on the generated code + IncludeTags []string `yaml:"include-tags,omitempty"` // Only include operations that have one of these tags. Ignored when empty. + ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty. + IncludeOperationIDs []string `yaml:"include-operation-ids,omitempty"` // Only include operations that have one of these operation-ids. Ignored when empty. + ExcludeOperationIDs []string `yaml:"exclude-operation-ids,omitempty"` // Exclude operations that have one of these operation-ids. Ignored when empty. + UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files ExcludeSchemas []string `yaml:"exclude-schemas,omitempty"` // Exclude from generation schemas with given names. Ignored when empty. ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types diff --git a/pkg/codegen/filter.go b/pkg/codegen/filter.go index a969badc7..435968a37 100644 --- a/pkg/codegen/filter.go +++ b/pkg/codegen/filter.go @@ -2,20 +2,24 @@ package codegen import "github.com/getkin/kin-openapi/openapi3" +func sliceToMap(items []string) map[string]bool { + m := make(map[string]bool, len(items)) + for _, item := range items { + m[item] = true + } + return m +} + func filterOperationsByTag(swagger *openapi3.T, opts Configuration) { if len(opts.OutputOptions.ExcludeTags) > 0 { - excludeOperationsWithTags(swagger.Paths, opts.OutputOptions.ExcludeTags) + operationsWithTags(swagger.Paths, sliceToMap(opts.OutputOptions.ExcludeTags), true) } if len(opts.OutputOptions.IncludeTags) > 0 { - includeOperationsWithTags(swagger.Paths, opts.OutputOptions.IncludeTags, false) + operationsWithTags(swagger.Paths, sliceToMap(opts.OutputOptions.IncludeTags), false) } } -func excludeOperationsWithTags(paths *openapi3.Paths, tags []string) { - includeOperationsWithTags(paths, tags, true) -} - -func includeOperationsWithTags(paths *openapi3.Paths, tags []string, exclude bool) { +func operationsWithTags(paths *openapi3.Paths, tags map[string]bool, exclude bool) { if paths == nil { return } @@ -35,16 +39,50 @@ func includeOperationsWithTags(paths *openapi3.Paths, tags []string, exclude boo } // operationHasTag returns true if the operation is tagged with any of tags -func operationHasTag(op *openapi3.Operation, tags []string) bool { +func operationHasTag(op *openapi3.Operation, tags map[string]bool) bool { if op == nil { return false } for _, hasTag := range op.Tags { - for _, wantTag := range tags { - if hasTag == wantTag { - return true - } + if tags[hasTag] { + return true } } return false } + +func filterOperationsByOperationID(swagger *openapi3.T, opts Configuration) { + if len(opts.OutputOptions.ExcludeOperationIDs) > 0 { + operationsWithOperationIDs(swagger.Paths, sliceToMap(opts.OutputOptions.ExcludeOperationIDs), true) + } + if len(opts.OutputOptions.IncludeOperationIDs) > 0 { + operationsWithOperationIDs(swagger.Paths, sliceToMap(opts.OutputOptions.IncludeOperationIDs), false) + } +} + +func operationsWithOperationIDs(paths *openapi3.Paths, operationIDs map[string]bool, exclude bool) { + if paths == nil { + return + } + + for _, pathItem := range paths.Map() { + ops := pathItem.Operations() + names := make([]string, 0, len(ops)) + for name, op := range ops { + if operationHasOperationID(op, operationIDs) == exclude { + names = append(names, name) + } + } + for _, name := range names { + pathItem.SetOperation(name, nil) + } + } +} + +// operationHasOperationID returns true if the operation has operation id is included in operation ids +func operationHasOperationID(op *openapi3.Operation, operationIDs map[string]bool) bool { + if op == nil { + return false + } + return operationIDs[op.OperationID] +} diff --git a/pkg/codegen/filter_test.go b/pkg/codegen/filter_test.go index 774d016a8..09dcf5c5a 100644 --- a/pkg/codegen/filter_test.go +++ b/pkg/codegen/filter_test.go @@ -67,3 +67,64 @@ func TestFilterOperationsByTag(t *testing.T) { assert.NotContains(t, code, `"/cat"`) }) } + +func TestFilterOperationsByOperationID(t *testing.T) { + packageName := "testswagger" + t.Run("include operation ids", func(t *testing.T) { + opts := Configuration{ + PackageName: packageName, + Generate: GenerateOptions{ + EchoServer: true, + Client: true, + Models: true, + EmbeddedSpec: true, + }, + OutputOptions: OutputOptions{ + IncludeOperationIDs: []string{"getCatStatus"}, + }, + } + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + + // Get a spec from the test definition in this file: + swagger, err := loader.LoadFromData([]byte(testOpenAPIDefinition)) + assert.NoError(t, err) + + // Run our code generation: + code, err := Generate(swagger, opts) + assert.NoError(t, err) + assert.NotEmpty(t, code) + assert.NotContains(t, code, `"/test/:name"`) + assert.Contains(t, code, `"/cat"`) + }) + + t.Run("exclude operation ids", func(t *testing.T) { + opts := Configuration{ + PackageName: packageName, + Generate: GenerateOptions{ + EchoServer: true, + Client: true, + Models: true, + EmbeddedSpec: true, + }, + OutputOptions: OutputOptions{ + ExcludeOperationIDs: []string{"getCatStatus"}, + }, + } + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + + // Get a spec from the test definition in this file: + swagger, err := loader.LoadFromData([]byte(testOpenAPIDefinition)) + assert.NoError(t, err) + + // Run our code generation: + code, err := Generate(swagger, opts) + assert.NoError(t, err) + assert.NotEmpty(t, code) + assert.Contains(t, code, `"/test/:name"`) + assert.NotContains(t, code, `"/cat"`) + }) +} From eb0c4cd489444b76b31180e7cd5efe42f780ea20 Mon Sep 17 00:00:00 2001 From: Oleksandr Redko Date: Thu, 25 Jan 2024 13:42:28 +0200 Subject: [PATCH 21/25] Fix typos in test fields and comments (#1287) Co-authored-by: Jamie Tanna --- README.md | 2 +- cmd/oapi-codegen/oapi-codegen.go | 6 +- internal/test/components/components.gen.go | 2 +- internal/test/components/components.yaml | 2 +- internal/test/issues/issue-1219/issue.gen.go | 96 ++++++++++---------- internal/test/issues/issue-1219/issue.yaml | 81 ++++++++--------- pkg/codegen/schema.go | 2 +- pkg/securityprovider/securityprovider.go | 2 +- 8 files changed, 96 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index c67d92011..6f4c1c840 100644 --- a/README.md +++ b/README.md @@ -619,7 +619,7 @@ which help you to use the various OpenAPI 3 Authentication mechanism. } // Example providing your own provider using an anonymous function wrapping in the - // InterceptoFn adapter. The behaviour between the InterceptorFn and the Interceptor interface + // InterceptorFn adapter. The behaviour between the InterceptorFn and the Interceptor interface // are the same as http.HandlerFunc and http.Handler. customProvider := func(req *http.Request, ctx context.Context) error { // Just log the request header, nothing else. diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go index 0c8acf2dc..3864879ca 100644 --- a/cmd/oapi-codegen/oapi-codegen.go +++ b/cmd/oapi-codegen/oapi-codegen.go @@ -55,7 +55,7 @@ var ( flagExcludeSchemas string flagResponseTypeSuffix string flagAliasTypes bool - flagInitalismOverrides bool + flagInitialismOverrides bool ) type configuration struct { @@ -109,7 +109,7 @@ func main() { flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation.") flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "The suffix used for responses types.") flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible.") - flag.BoolVar(&flagInitalismOverrides, "initialism-overrides", false, "Use initialism overrides.") + flag.BoolVar(&flagInitialismOverrides, "initialism-overrides", false, "Use initialism overrides.") flag.Parse() @@ -429,7 +429,7 @@ func updateConfigFromFlags(cfg *configuration) error { cfg.OutputFile = flagOutputFile } - cfg.OutputOptions.InitialismOverrides = flagInitalismOverrides + cfg.OutputOptions.InitialismOverrides = flagInitialismOverrides return nil } diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go index 3a192870c..be856de10 100644 --- a/internal/test/components/components.gen.go +++ b/internal/test/components/components.gen.go @@ -293,7 +293,7 @@ type OneOfObject8 struct { union json.RawMessage } -// OneOfObject9 oneOf with fixed descriminator +// OneOfObject9 oneOf with fixed discriminator type OneOfObject9 struct { Type string `json:"type"` union json.RawMessage diff --git a/internal/test/components/components.yaml b/internal/test/components/components.yaml index f755a6a35..f711510e3 100644 --- a/internal/test/components/components.yaml +++ b/internal/test/components/components.yaml @@ -321,7 +321,7 @@ components: - $ref: '#/components/schemas/OneOfVariant1' - $ref: '#/components/schemas/OneOfVariant2' OneOfObject9: - description: oneOf with fixed descriminator + description: oneOf with fixed discriminator type: object properties: type: diff --git a/internal/test/issues/issue-1219/issue.gen.go b/internal/test/issues/issue-1219/issue.gen.go index d0935eca7..e8df58f14 100644 --- a/internal/test/issues/issue-1219/issue.gen.go +++ b/internal/test/issues/issue-1219/issue.gen.go @@ -8,14 +8,14 @@ import ( "fmt" ) -// DefaultAddtional1 defines model for DefaultAddtional1. -type DefaultAddtional1 struct { +// DefaultAdditional1 defines model for DefaultAdditional1. +type DefaultAdditional1 struct { Field1 *int `json:"field1,omitempty"` Field2 *string `json:"field2,omitempty"` } -// DefaultAddtional2 defines model for DefaultAddtional2. -type DefaultAddtional2 struct { +// DefaultAdditional2 defines model for DefaultAdditional2. +type DefaultAdditional2 struct { FieldA *int `json:"fieldA,omitempty"` FieldB *string `json:"fieldB,omitempty"` } @@ -147,42 +147,42 @@ type MergeWithoutWithout struct { FieldB *string `json:"fieldB,omitempty"` } -// WithAnyAddtional1 defines model for WithAnyAddtional1. -type WithAnyAddtional1 struct { +// WithAnyAdditional1 defines model for WithAnyAdditional1. +type WithAnyAdditional1 struct { Field1 *int `json:"field1,omitempty"` Field2 *string `json:"field2,omitempty"` AdditionalProperties map[string]interface{} `json:"-"` } -// WithAnyAddtional2 defines model for WithAnyAddtional2. -type WithAnyAddtional2 struct { +// WithAnyAdditional2 defines model for WithAnyAdditional2. +type WithAnyAdditional2 struct { FieldA *int `json:"fieldA,omitempty"` FieldB *string `json:"fieldB,omitempty"` AdditionalProperties map[string]interface{} `json:"-"` } -// WithStringAddtional1 defines model for WithStringAddtional1. -type WithStringAddtional1 struct { +// WithStringAdditional1 defines model for WithStringAdditional1. +type WithStringAdditional1 struct { Field1 *int `json:"field1,omitempty"` Field2 *string `json:"field2,omitempty"` AdditionalProperties map[string]string `json:"-"` } -// WithStringAddtional2 defines model for WithStringAddtional2. -type WithStringAddtional2 struct { +// WithStringAdditional2 defines model for WithStringAdditional2. +type WithStringAdditional2 struct { FieldA *int `json:"fieldA,omitempty"` FieldB *string `json:"fieldB,omitempty"` AdditionalProperties map[string]string `json:"-"` } -// WithoutAddtional1 defines model for WithoutAddtional1. -type WithoutAddtional1 struct { +// WithoutAdditional1 defines model for WithoutAdditional1. +type WithoutAdditional1 struct { Field1 *int `json:"field1,omitempty"` Field2 *string `json:"field2,omitempty"` } -// WithoutAddtional2 defines model for WithoutAddtional2. -type WithoutAddtional2 struct { +// WithoutAdditional2 defines model for WithoutAdditional2. +type WithoutAdditional2 struct { FieldA *int `json:"fieldA,omitempty"` FieldB *string `json:"fieldB,omitempty"` } @@ -978,25 +978,25 @@ func (a MergeWithStringWithAny) MarshalJSON() ([]byte, error) { return json.Marshal(object) } -// Getter for additional properties for WithAnyAddtional1. Returns the specified +// Getter for additional properties for WithAnyAdditional1. Returns the specified // element and whether it was found -func (a WithAnyAddtional1) Get(fieldName string) (value interface{}, found bool) { +func (a WithAnyAdditional1) Get(fieldName string) (value interface{}, found bool) { if a.AdditionalProperties != nil { value, found = a.AdditionalProperties[fieldName] } return } -// Setter for additional properties for WithAnyAddtional1 -func (a *WithAnyAddtional1) Set(fieldName string, value interface{}) { +// Setter for additional properties for WithAnyAdditional1 +func (a *WithAnyAdditional1) Set(fieldName string, value interface{}) { if a.AdditionalProperties == nil { a.AdditionalProperties = make(map[string]interface{}) } a.AdditionalProperties[fieldName] = value } -// Override default JSON handling for WithAnyAddtional1 to handle AdditionalProperties -func (a *WithAnyAddtional1) UnmarshalJSON(b []byte) error { +// Override default JSON handling for WithAnyAdditional1 to handle AdditionalProperties +func (a *WithAnyAdditional1) UnmarshalJSON(b []byte) error { object := make(map[string]json.RawMessage) err := json.Unmarshal(b, &object) if err != nil { @@ -1033,8 +1033,8 @@ func (a *WithAnyAddtional1) UnmarshalJSON(b []byte) error { return nil } -// Override default JSON handling for WithAnyAddtional1 to handle AdditionalProperties -func (a WithAnyAddtional1) MarshalJSON() ([]byte, error) { +// Override default JSON handling for WithAnyAdditional1 to handle AdditionalProperties +func (a WithAnyAdditional1) MarshalJSON() ([]byte, error) { var err error object := make(map[string]json.RawMessage) @@ -1061,25 +1061,25 @@ func (a WithAnyAddtional1) MarshalJSON() ([]byte, error) { return json.Marshal(object) } -// Getter for additional properties for WithAnyAddtional2. Returns the specified +// Getter for additional properties for WithAnyAdditional2. Returns the specified // element and whether it was found -func (a WithAnyAddtional2) Get(fieldName string) (value interface{}, found bool) { +func (a WithAnyAdditional2) Get(fieldName string) (value interface{}, found bool) { if a.AdditionalProperties != nil { value, found = a.AdditionalProperties[fieldName] } return } -// Setter for additional properties for WithAnyAddtional2 -func (a *WithAnyAddtional2) Set(fieldName string, value interface{}) { +// Setter for additional properties for WithAnyAdditional2 +func (a *WithAnyAdditional2) Set(fieldName string, value interface{}) { if a.AdditionalProperties == nil { a.AdditionalProperties = make(map[string]interface{}) } a.AdditionalProperties[fieldName] = value } -// Override default JSON handling for WithAnyAddtional2 to handle AdditionalProperties -func (a *WithAnyAddtional2) UnmarshalJSON(b []byte) error { +// Override default JSON handling for WithAnyAdditional2 to handle AdditionalProperties +func (a *WithAnyAdditional2) UnmarshalJSON(b []byte) error { object := make(map[string]json.RawMessage) err := json.Unmarshal(b, &object) if err != nil { @@ -1116,8 +1116,8 @@ func (a *WithAnyAddtional2) UnmarshalJSON(b []byte) error { return nil } -// Override default JSON handling for WithAnyAddtional2 to handle AdditionalProperties -func (a WithAnyAddtional2) MarshalJSON() ([]byte, error) { +// Override default JSON handling for WithAnyAdditional2 to handle AdditionalProperties +func (a WithAnyAdditional2) MarshalJSON() ([]byte, error) { var err error object := make(map[string]json.RawMessage) @@ -1144,25 +1144,25 @@ func (a WithAnyAddtional2) MarshalJSON() ([]byte, error) { return json.Marshal(object) } -// Getter for additional properties for WithStringAddtional1. Returns the specified +// Getter for additional properties for WithStringAdditional1. Returns the specified // element and whether it was found -func (a WithStringAddtional1) Get(fieldName string) (value string, found bool) { +func (a WithStringAdditional1) Get(fieldName string) (value string, found bool) { if a.AdditionalProperties != nil { value, found = a.AdditionalProperties[fieldName] } return } -// Setter for additional properties for WithStringAddtional1 -func (a *WithStringAddtional1) Set(fieldName string, value string) { +// Setter for additional properties for WithStringAdditional1 +func (a *WithStringAdditional1) Set(fieldName string, value string) { if a.AdditionalProperties == nil { a.AdditionalProperties = make(map[string]string) } a.AdditionalProperties[fieldName] = value } -// Override default JSON handling for WithStringAddtional1 to handle AdditionalProperties -func (a *WithStringAddtional1) UnmarshalJSON(b []byte) error { +// Override default JSON handling for WithStringAdditional1 to handle AdditionalProperties +func (a *WithStringAdditional1) UnmarshalJSON(b []byte) error { object := make(map[string]json.RawMessage) err := json.Unmarshal(b, &object) if err != nil { @@ -1199,8 +1199,8 @@ func (a *WithStringAddtional1) UnmarshalJSON(b []byte) error { return nil } -// Override default JSON handling for WithStringAddtional1 to handle AdditionalProperties -func (a WithStringAddtional1) MarshalJSON() ([]byte, error) { +// Override default JSON handling for WithStringAdditional1 to handle AdditionalProperties +func (a WithStringAdditional1) MarshalJSON() ([]byte, error) { var err error object := make(map[string]json.RawMessage) @@ -1227,25 +1227,25 @@ func (a WithStringAddtional1) MarshalJSON() ([]byte, error) { return json.Marshal(object) } -// Getter for additional properties for WithStringAddtional2. Returns the specified +// Getter for additional properties for WithStringAdditional2. Returns the specified // element and whether it was found -func (a WithStringAddtional2) Get(fieldName string) (value string, found bool) { +func (a WithStringAdditional2) Get(fieldName string) (value string, found bool) { if a.AdditionalProperties != nil { value, found = a.AdditionalProperties[fieldName] } return } -// Setter for additional properties for WithStringAddtional2 -func (a *WithStringAddtional2) Set(fieldName string, value string) { +// Setter for additional properties for WithStringAdditional2 +func (a *WithStringAdditional2) Set(fieldName string, value string) { if a.AdditionalProperties == nil { a.AdditionalProperties = make(map[string]string) } a.AdditionalProperties[fieldName] = value } -// Override default JSON handling for WithStringAddtional2 to handle AdditionalProperties -func (a *WithStringAddtional2) UnmarshalJSON(b []byte) error { +// Override default JSON handling for WithStringAdditional2 to handle AdditionalProperties +func (a *WithStringAdditional2) UnmarshalJSON(b []byte) error { object := make(map[string]json.RawMessage) err := json.Unmarshal(b, &object) if err != nil { @@ -1282,8 +1282,8 @@ func (a *WithStringAddtional2) UnmarshalJSON(b []byte) error { return nil } -// Override default JSON handling for WithStringAddtional2 to handle AdditionalProperties -func (a WithStringAddtional2) MarshalJSON() ([]byte, error) { +// Override default JSON handling for WithStringAdditional2 to handle AdditionalProperties +func (a WithStringAdditional2) MarshalJSON() ([]byte, error) { var err error object := make(map[string]json.RawMessage) diff --git a/internal/test/issues/issue-1219/issue.yaml b/internal/test/issues/issue-1219/issue.yaml index 6ded9d927..043742327 100644 --- a/internal/test/issues/issue-1219/issue.yaml +++ b/internal/test/issues/issue-1219/issue.yaml @@ -1,7 +1,7 @@ openapi: "3.0.1" components: schemas: - WithAnyAddtional1: + WithAnyAdditional1: type: object properties: field1: @@ -9,7 +9,7 @@ components: field2: type: string additionalProperties: true - WithAnyAddtional2: + WithAnyAdditional2: type: object properties: fieldA: @@ -17,7 +17,7 @@ components: fieldB: type: string additionalProperties: true - WithStringAddtional1: + WithStringAdditional1: type: object properties: field1: @@ -26,7 +26,7 @@ components: type: string additionalProperties: type: string - WithStringAddtional2: + WithStringAdditional2: type: object properties: fieldA: @@ -35,7 +35,7 @@ components: type: string additionalProperties: type: string - WithoutAddtional1: + WithoutAdditional1: type: object properties: field1: @@ -43,7 +43,7 @@ components: field2: type: string additionalProperties: false - WithoutAddtional2: + WithoutAdditional2: type: object properties: fieldA: @@ -51,14 +51,14 @@ components: fieldB: type: string additionalProperties: false - DefaultAddtional1: + DefaultAdditional1: type: object properties: field1: type: integer field2: type: string - DefaultAddtional2: + DefaultAdditional2: type: object properties: fieldA: @@ -68,70 +68,69 @@ components: MergeWithoutWithout: allOf: - - $ref: '#/components/schemas/WithoutAddtional1' - - $ref: '#/components/schemas/WithoutAddtional2' + - $ref: '#/components/schemas/WithoutAdditional1' + - $ref: '#/components/schemas/WithoutAdditional2' MergeWithoutWithString: allOf: - - $ref: '#/components/schemas/WithoutAddtional1' - - $ref: '#/components/schemas/WithStringAddtional2' + - $ref: '#/components/schemas/WithoutAdditional1' + - $ref: '#/components/schemas/WithStringAdditional2' MergeWithoutWithAny: allOf: - - $ref: '#/components/schemas/WithoutAddtional1' - - $ref: '#/components/schemas/WithAnyAddtional2' + - $ref: '#/components/schemas/WithoutAdditional1' + - $ref: '#/components/schemas/WithAnyAdditional2' MergeWithoutDefault: allOf: - - $ref: '#/components/schemas/WithoutAddtional1' - - $ref: '#/components/schemas/DefaultAddtional2' + - $ref: '#/components/schemas/WithoutAdditional1' + - $ref: '#/components/schemas/DefaultAdditional2' MergeWithStringWithout: allOf: - - $ref: '#/components/schemas/WithStringAddtional1' - - $ref: '#/components/schemas/WithoutAddtional2' + - $ref: '#/components/schemas/WithStringAdditional1' + - $ref: '#/components/schemas/WithoutAdditional2' # Cannot merge this # MergeWithStringWithString: # allOf: - # - $ref: '#/components/schemas/WithStringAddtional1' - # - $ref: '#/components/schemas/WithStringAddtional2' + # - $ref: '#/components/schemas/WithStringAdditional1' + # - $ref: '#/components/schemas/WithStringAdditional2' MergeWithStringWithAny: allOf: - - $ref: '#/components/schemas/WithStringAddtional1' - - $ref: '#/components/schemas/WithAnyAddtional2' + - $ref: '#/components/schemas/WithStringAdditional1' + - $ref: '#/components/schemas/WithAnyAdditional2' MergeWithStringDefault: allOf: - - $ref: '#/components/schemas/WithStringAddtional1' - - $ref: '#/components/schemas/DefaultAddtional2' + - $ref: '#/components/schemas/WithStringAdditional1' + - $ref: '#/components/schemas/DefaultAdditional2' MergeWithAnyWithout: allOf: - - $ref: '#/components/schemas/WithAnyAddtional1' - - $ref: '#/components/schemas/WithoutAddtional2' + - $ref: '#/components/schemas/WithAnyAdditional1' + - $ref: '#/components/schemas/WithoutAdditional2' MergeWithAnyWithString: allOf: - - $ref: '#/components/schemas/WithAnyAddtional1' - - $ref: '#/components/schemas/WithStringAddtional2' + - $ref: '#/components/schemas/WithAnyAdditional1' + - $ref: '#/components/schemas/WithStringAdditional2' MergeWithAnyWithAny: allOf: - - $ref: '#/components/schemas/WithAnyAddtional1' - - $ref: '#/components/schemas/WithAnyAddtional2' + - $ref: '#/components/schemas/WithAnyAdditional1' + - $ref: '#/components/schemas/WithAnyAdditional2' MergeWithAnyDefault: allOf: - - $ref: '#/components/schemas/WithAnyAddtional1' - - $ref: '#/components/schemas/DefaultAddtional2' + - $ref: '#/components/schemas/WithAnyAdditional1' + - $ref: '#/components/schemas/DefaultAdditional2' MergeDefaultWithout: allOf: - - $ref: '#/components/schemas/DefaultAddtional1' - - $ref: '#/components/schemas/WithoutAddtional2' + - $ref: '#/components/schemas/DefaultAdditional1' + - $ref: '#/components/schemas/WithoutAdditional2' MergeDefaultWithString: allOf: - - $ref: '#/components/schemas/DefaultAddtional1' - - $ref: '#/components/schemas/WithStringAddtional2' + - $ref: '#/components/schemas/DefaultAdditional1' + - $ref: '#/components/schemas/WithStringAdditional2' MergeDefaultWithAny: allOf: - - $ref: '#/components/schemas/DefaultAddtional1' - - $ref: '#/components/schemas/WithAnyAddtional2' + - $ref: '#/components/schemas/DefaultAdditional1' + - $ref: '#/components/schemas/WithAnyAdditional2' MergeDefaultDefault: allOf: - - $ref: '#/components/schemas/DefaultAddtional1' - - $ref: '#/components/schemas/DefaultAddtional2' - + - $ref: '#/components/schemas/DefaultAdditional1' + - $ref: '#/components/schemas/DefaultAdditional2' diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 87d5a2501..500b28730 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -60,7 +60,7 @@ func (s Schema) TypeDecl() string { // AddProperty adds a new property to the current Schema, and returns an error // if it collides. Two identical fields will not collide, but two properties by // the same name, but different definition, will collide. It's safe to merge the -// fields of two schemas with overalapping properties if those properties are +// fields of two schemas with overlapping properties if those properties are // identical. func (s *Schema) AddProperty(p Property) error { // Scan all existing properties for a conflict diff --git a/pkg/securityprovider/securityprovider.go b/pkg/securityprovider/securityprovider.go index 940fc5b47..091c1765e 100644 --- a/pkg/securityprovider/securityprovider.go +++ b/pkg/securityprovider/securityprovider.go @@ -47,7 +47,7 @@ func (s *SecurityProviderBasicAuth) Intercept(ctx context.Context, req *http.Req } // NewSecurityProviderBearerToken provides a SecurityProvider, which can solve -// the Bearer Auth challende for api-calls. +// the Bearer Auth challenge for api-calls. func NewSecurityProviderBearerToken(token string) (*SecurityProviderBearerToken, error) { return &SecurityProviderBearerToken{ token: token, From e5f477d1b550371fd76f5baaadb4577cb2c6fc39 Mon Sep 17 00:00:00 2001 From: mtskg Date: Thu, 25 Jan 2024 19:12:45 +0700 Subject: [PATCH 22/25] Fixes confusing indentation for example code in README.md (#1414) Fixes indentation for "Registering handlers" example code for Gin. Fixes indentation in README example code. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6f4c1c840..17b04f3be 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ func SetupHandler() { var myApi PetStoreImpl r := gin.Default() - r.Use(middleware.OapiRequestValidator(swagger)) + r.Use(middleware.OapiRequestValidator(swagger)) r = api.RegisterHandlers(r, petStore) } ``` @@ -343,7 +343,7 @@ func SetupHandler() { var myApi PetStoreImpl i := iris.Default() - i.Use(middleware.OapiRequestValidator(swagger)) + i.Use(middleware.OapiRequestValidator(swagger)) api.RegisterHandlers(r, &myApi) } ``` @@ -383,7 +383,7 @@ The generated strict wrapper can then be used as an implementation for `ServerIn ```go func SetupHandler() { var myApi PetStoreImpl - myStrictApiHandler := api.NewStrictHandler(myApi, nil) + myStrictApiHandler := api.NewStrictHandler(myApi, nil) e := echo.New() petstore.RegisterHandlers(e, &myStrictApiHandler) } From 5cdb5595bd38525b0a8f19cd322b91c0fb34b4d6 Mon Sep 17 00:00:00 2001 From: Kean Johnston Date: Fri, 8 Sep 2023 13:42:20 +0200 Subject: [PATCH 23/25] Allow disabling type aliases for arrays As noted in #1247, in some cases it can be useful to have arrays defined as a concrete type, not a type alias, so you are able to extend them with methods. This produces a new opt-in output-option, `disable-type-aliases-for-type`, which can be used to control this behaviour Right now, we only want to add support for `array`s, but the configuration is extensible in the future. This also introduces a test case to demonstrate the capabilities. We need to introduce a `sliceContains` method to simplify this lookup, as we don't yet have `slices.Contains` due to the Go versions we're targeting. --- .../outputoptions/disabletypealiases/config.yaml | 8 ++++++++ .../outputoptions/disabletypealiases/generate.go | 3 +++ .../test/outputoptions/disabletypealiases/spec.yaml | 13 +++++++++++++ .../outputoptions/disabletypealiases/types.gen.go | 13 +++++++++++++ .../outputoptions/disabletypealiases/types_ext.go | 5 +++++ pkg/codegen/configuration.go | 5 +++++ pkg/codegen/schema.go | 6 ++++++ pkg/codegen/utils.go | 9 +++++++++ 8 files changed, 62 insertions(+) create mode 100644 internal/test/outputoptions/disabletypealiases/config.yaml create mode 100644 internal/test/outputoptions/disabletypealiases/generate.go create mode 100644 internal/test/outputoptions/disabletypealiases/spec.yaml create mode 100644 internal/test/outputoptions/disabletypealiases/types.gen.go create mode 100644 internal/test/outputoptions/disabletypealiases/types_ext.go diff --git a/internal/test/outputoptions/disabletypealiases/config.yaml b/internal/test/outputoptions/disabletypealiases/config.yaml new file mode 100644 index 000000000..615bfcda7 --- /dev/null +++ b/internal/test/outputoptions/disabletypealiases/config.yaml @@ -0,0 +1,8 @@ +package: types +generate: + models: true +output: types.gen.go +output-options: + skip-prune: true + disable-type-aliases-for-type: + - array diff --git a/internal/test/outputoptions/disabletypealiases/generate.go b/internal/test/outputoptions/disabletypealiases/generate.go new file mode 100644 index 000000000..8043e48f5 --- /dev/null +++ b/internal/test/outputoptions/disabletypealiases/generate.go @@ -0,0 +1,3 @@ +package types + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml diff --git a/internal/test/outputoptions/disabletypealiases/spec.yaml b/internal/test/outputoptions/disabletypealiases/spec.yaml new file mode 100644 index 000000000..e073eb3c6 --- /dev/null +++ b/internal/test/outputoptions/disabletypealiases/spec.yaml @@ -0,0 +1,13 @@ +components: + schemas: + my_item: + type: object + properties: + name: + type: string + age: + type: integer + example: + type: array + items: + $ref: '#/components/schemas/my_item' diff --git a/internal/test/outputoptions/disabletypealiases/types.gen.go b/internal/test/outputoptions/disabletypealiases/types.gen.go new file mode 100644 index 000000000..8bb7b1ef5 --- /dev/null +++ b/internal/test/outputoptions/disabletypealiases/types.gen.go @@ -0,0 +1,13 @@ +// Package types provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package types + +// Example defines model for example. +type Example []MyItem + +// MyItem defines model for my_item. +type MyItem struct { + Age *int `json:"age,omitempty"` + Name *string `json:"name,omitempty"` +} diff --git a/internal/test/outputoptions/disabletypealiases/types_ext.go b/internal/test/outputoptions/disabletypealiases/types_ext.go new file mode 100644 index 000000000..49103fd7f --- /dev/null +++ b/internal/test/outputoptions/disabletypealiases/types_ext.go @@ -0,0 +1,5 @@ +package types + +// MustCompile will only compile if the type it's defined on has type aliases disabled +func (*Example) MustCompile() { +} diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go index efacfb7c7..3377df627 100644 --- a/pkg/codegen/configuration.go +++ b/pkg/codegen/configuration.go @@ -101,6 +101,11 @@ type OutputOptions struct { ClientTypeName string `yaml:"client-type-name,omitempty"` // Override the default generated client type with the value InitialismOverrides bool `yaml:"initialism-overrides,omitempty"` // Whether to use the initialism overrides NullableType bool `yaml:"nullable-type,omitempty"` // Whether to generate nullable type for nullable fields + + // DisableTypeAliasesForType allows defining which OpenAPI `type`s will explicitly not use type aliases + // Currently supports: + // "array" + DisableTypeAliasesForType []string `yaml:"disable-type-aliases-for-type"` } // UpdateDefaults sets reasonable default values for unset fields in Configuration diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go index 500b28730..560bc360f 100644 --- a/pkg/codegen/schema.go +++ b/pkg/codegen/schema.go @@ -33,6 +33,8 @@ type Schema struct { // If this is set, the schema will declare a type via alias, eg, // `type Foo = bool`. If this is not set, we will define this type via // type definition `type Foo bool` + // + // Can be overriden by the OutputOptions#DisableTypeAliasesForType field DefineViaAlias bool // The original OpenAPIv3 Schema. @@ -559,6 +561,10 @@ func oapiSchemaToGoType(schema *openapi3.Schema, path []string, outSchema *Schem outSchema.AdditionalTypes = arrayType.AdditionalTypes outSchema.Properties = arrayType.Properties outSchema.DefineViaAlias = true + if sliceContains(globalState.options.OutputOptions.DisableTypeAliasesForType, "array") { + outSchema.DefineViaAlias = false + } + case "integer": // We default to int if format doesn't ask for something else. if f == "int64" { diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go index 49a2f53f2..a1fb62ffd 100644 --- a/pkg/codegen/utils.go +++ b/pkg/codegen/utils.go @@ -963,3 +963,12 @@ func isAdditionalPropertiesExplicitFalse(s *openapi3.Schema) bool { return *s.AdditionalProperties.Has == false //nolint:gosimple } + +func sliceContains[E comparable](s []E, v E) bool { + for _, ss := range s { + if ss == v { + return true + } + } + return false +} From 3bf5755366704a1594410d6a7aa7aab966d04c6e Mon Sep 17 00:00:00 2001 From: Filinto Duran Date: Thu, 25 Jan 2024 08:34:47 -0600 Subject: [PATCH 24/25] Add support for x-order extension (#1190) As noted in #428, there are some cases where it can be useful to set the field ordering. This adds support for the `x-order` extension used by other OpenAPI parsers and generators, and ensures that we return keys sorted by that order, if present. This also adds a relevant test case + unit tests to validate functionality. Closes #428. Co-authored-by: Filinto Duran Signed-off-by: Filinto Duran Co-authored-by: Andrew Rusakow Co-authored-by: Jamie Tanna --- README.md | 29 +++++++ internal/test/extensions/x-order/config.yaml | 6 ++ internal/test/extensions/x-order/generate.go | 3 + internal/test/extensions/x-order/issue.gen.go | 14 ++++ internal/test/extensions/x-order/spec.yaml | 15 ++++ pkg/codegen/utils.go | 28 ++++++- pkg/codegen/utils_test.go | 76 +++++++++++++++++++ 7 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 internal/test/extensions/x-order/config.yaml create mode 100644 internal/test/extensions/x-order/generate.go create mode 100644 internal/test/extensions/x-order/issue.gen.go create mode 100644 internal/test/extensions/x-order/spec.yaml diff --git a/README.md b/README.md index 17b04f3be..aaa94da28 100644 --- a/README.md +++ b/README.md @@ -779,6 +779,35 @@ which help you to use the various OpenAPI 3 Authentication mechanism. type ObjectCategory int ``` +- `x-order`: specifies the order of the fields in the structure. It allows you to specify the order + of the fields in the generated structure and will override any default value. This extended + property is not supported in all parts of OpenAPI, so check the specification to see where it is + allowed. + + ```yaml + DateInterval: + type: object + required: + - name + properties: + end: + type: string + format: date + x-order: 2 + start: + type: string + format: date + x-order: 1 + ``` + In the example above, struct will be declared as: + + ```go + type DateInterval struct { + Start *openapi_types.Date `json:"start,omitempty"` + End *openapi_types.Date `json:"end,omitempty"` + } + ``` + ## Using `oapi-codegen` The default options for `oapi-codegen` will generate everything; client, server, diff --git a/internal/test/extensions/x-order/config.yaml b/internal/test/extensions/x-order/config.yaml new file mode 100644 index 000000000..d5db754a1 --- /dev/null +++ b/internal/test/extensions/x-order/config.yaml @@ -0,0 +1,6 @@ +package: xorder +generate: + models: true +output: issue.gen.go +output-options: + skip-prune: true diff --git a/internal/test/extensions/x-order/generate.go b/internal/test/extensions/x-order/generate.go new file mode 100644 index 000000000..d49bb7970 --- /dev/null +++ b/internal/test/extensions/x-order/generate.go @@ -0,0 +1,3 @@ +package xorder + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml spec.yaml diff --git a/internal/test/extensions/x-order/issue.gen.go b/internal/test/extensions/x-order/issue.gen.go new file mode 100644 index 000000000..6934cea8f --- /dev/null +++ b/internal/test/extensions/x-order/issue.gen.go @@ -0,0 +1,14 @@ +// Package xorder provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package xorder + +import ( + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// DateInterval defines model for DateInterval. +type DateInterval struct { + Start *openapi_types.Date `json:"start,omitempty"` + End *openapi_types.Date `json:"end,omitempty"` +} diff --git a/internal/test/extensions/x-order/spec.yaml b/internal/test/extensions/x-order/spec.yaml new file mode 100644 index 000000000..49b625ec1 --- /dev/null +++ b/internal/test/extensions/x-order/spec.yaml @@ -0,0 +1,15 @@ +components: + schemas: + DateInterval: + type: object + required: + - name + properties: + end: + type: string + format: date + x-order: 2 + start: + type: string + format: date + x-order: 1 diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go index a1fb62ffd..16d92ac74 100644 --- a/pkg/codegen/utils.go +++ b/pkg/codegen/utils.go @@ -194,12 +194,34 @@ func mediaTypeToCamelCase(s string) string { // order, since Golang scrambles dictionary keys func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string { keys := make([]string, len(dict)) + orders := make(map[string]int64, len(dict)) i := 0 - for key := range dict { - keys[i] = key + + for key, v := range dict { + keys[i], orders[key] = key, int64(len(dict)) i++ + + if v == nil || v.Value == nil { + continue + } + + ext := v.Value.Extensions["x-order"] + if ext == nil { + continue + } + + // YAML parsing picks up the x-order as a float64 + if order, ok := ext.(float64); ok { + orders[key] = int64(order) + } } - sort.Strings(keys) + + sort.Slice(keys, func(i, j int) bool { + if i, j := orders[keys[i]], orders[keys[j]]; i != j { + return i < j + } + return keys[i] < keys[j] + }) return keys } diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go index c837ffe7e..85c279f1b 100644 --- a/pkg/codegen/utils_test.go +++ b/pkg/codegen/utils_test.go @@ -18,6 +18,7 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestStringOps(t *testing.T) { @@ -43,6 +44,81 @@ func TestSortedSchemaKeys(t *testing.T) { assert.EqualValues(t, expected, SortedSchemaKeys(dict), "Keys are not sorted properly") } +func TestSortedSchemaKeysWithXOrder(t *testing.T) { + withOrder := func(i float64) *openapi3.SchemaRef { + return &openapi3.SchemaRef{ + Value: &openapi3.Schema{ + Extensions: map[string]interface{}{"x-order": i}, + }, + } + } + dict := map[string]*openapi3.SchemaRef{ + "first": withOrder(1), + "minusTenth": withOrder(-10), + "zero": withOrder(0), + "minusHundredth_2": withOrder(-100), + "minusHundredth_1": withOrder(-100), + "afterFirst": withOrder(2), + "last": withOrder(100), + "middleA": nil, + "middleB": nil, + "middleC": nil, + } + + expected := []string{"minusHundredth_1", "minusHundredth_2", "minusTenth", "zero", "first", "afterFirst", "middleA", "middleB", "middleC", "last"} + + assert.EqualValues(t, expected, SortedSchemaKeys(dict), "Keys are not sorted properly") +} + +func TestSortedSchemaKeysWithXOrderFromParsed(t *testing.T) { + rawSpec := `--- +components: + schemas: + AlwaysLast: + type: string + x-order: 100000 + DateInterval: + type: object + required: + - name + properties: + end: + type: string + format: date + x-order: 2 + start: + type: string + format: date + x-order: 1 + ` + + loader := openapi3.NewLoader() + spec, err := loader.LoadFromData([]byte(rawSpec)) + require.NoError(t, err) + require.NotNil(t, spec.Components) + require.NotNil(t, spec.Components.Schemas) + + t.Run("for the top-level schemas", func(t *testing.T) { + expected := []string{"DateInterval", "AlwaysLast"} + + actual := SortedSchemaKeys(spec.Components.Schemas) + + assert.EqualValues(t, expected, actual) + }) + + t.Run("for DateInterval's keys", func(t *testing.T) { + schemas, found := spec.Components.Schemas["DateInterval"] + require.True(t, found, "did not find `#/components/schemas/DateInterval`") + + expected := []string{"start", "end"} + + actual := SortedSchemaKeys(schemas.Value.Properties) + + assert.EqualValues(t, expected, actual, "Keys are not sorted properly") + }) + +} + func TestSortedPathsKeys(t *testing.T) { dict := map[string]*openapi3.PathItem{ "f": nil, From ad5eada4f3ccc28a88477cef62ea21c17fc8aa01 Mon Sep 17 00:00:00 2001 From: Jamie Tanna Date: Thu, 25 Jan 2024 10:52:13 +0000 Subject: [PATCH 25/25] Fix: Ensure external refs are propagated to generated code As noted in #1378, there are cases where a complex set of `$ref`s between multiple files can lead to broken generated code, which does not correctly import the package that has been prepared for the external reference. We can handle this by looking up any references, where there is a `.Ref` passed into the type, and then iterate through relevant children. This requires we handle the updating in-place for these by using a bit of pointer + indexing fun. This also adds a relevant test case to validate the fix. Closes #1378. --- internal/test/issues/issue-1378/bionicle.yaml | 43 ++ .../issue-1378/bionicle/bionicle.gen.go | 372 ++++++++++++++++++ .../issues/issue-1378/bionicle/config.yaml | 9 + .../issues/issue-1378/bionicle/generate.go | 3 + internal/test/issues/issue-1378/common.yaml | 16 + .../issues/issue-1378/common/common.gen.go | 262 ++++++++++++ .../test/issues/issue-1378/common/config.yaml | 9 + .../test/issues/issue-1378/common/generate.go | 3 + .../test/issues/issue-1378/foo-service.yaml | 10 + .../issues/issue-1378/fooservice/config.yaml | 10 + .../issue-1378/fooservice/fooservice.gen.go | 371 +++++++++++++++++ .../issues/issue-1378/fooservice/generate.go | 3 + pkg/codegen/externalref.go | 80 ++++ pkg/codegen/operations.go | 7 + 14 files changed, 1198 insertions(+) create mode 100644 internal/test/issues/issue-1378/bionicle.yaml create mode 100644 internal/test/issues/issue-1378/bionicle/bionicle.gen.go create mode 100644 internal/test/issues/issue-1378/bionicle/config.yaml create mode 100644 internal/test/issues/issue-1378/bionicle/generate.go create mode 100644 internal/test/issues/issue-1378/common.yaml create mode 100644 internal/test/issues/issue-1378/common/common.gen.go create mode 100644 internal/test/issues/issue-1378/common/config.yaml create mode 100644 internal/test/issues/issue-1378/common/generate.go create mode 100644 internal/test/issues/issue-1378/foo-service.yaml create mode 100644 internal/test/issues/issue-1378/fooservice/config.yaml create mode 100644 internal/test/issues/issue-1378/fooservice/fooservice.gen.go create mode 100644 internal/test/issues/issue-1378/fooservice/generate.go create mode 100644 pkg/codegen/externalref.go diff --git a/internal/test/issues/issue-1378/bionicle.yaml b/internal/test/issues/issue-1378/bionicle.yaml new file mode 100644 index 000000000..0013d121a --- /dev/null +++ b/internal/test/issues/issue-1378/bionicle.yaml @@ -0,0 +1,43 @@ +openapi: 3.0.1 + +info: + title: Test + description: Test + version: 1.0.0 + +paths: + /bionicle/{name}: + get: + parameters: + - $ref: "#/components/parameters/bionicleName" + responses: + '200': + content: + application/json: + schema: + $ref: '#/components/schemas/Bionicle' + '400': + content: + application/json: + schema: + oneOf: + - $ref: '#/components/schemas/Bionicle' + +components: + parameters: + bionicleName: + name: name + in: path + description: Name of the character + required: true + schema: + type: string + + schemas: + Bionicle: + type: object + properties: + name: + type: string + required: + - name diff --git a/internal/test/issues/issue-1378/bionicle/bionicle.gen.go b/internal/test/issues/issue-1378/bionicle/bionicle.gen.go new file mode 100644 index 000000000..4b41f81d0 --- /dev/null +++ b/internal/test/issues/issue-1378/bionicle/bionicle.gen.go @@ -0,0 +1,372 @@ +// Package bionicle provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package bionicle + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + externalRef0 "github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/common" + "github.com/getkin/kin-openapi/openapi3" + "github.com/gorilla/mux" + "github.com/oapi-codegen/runtime" + strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" +) + +// Bionicle defines model for Bionicle. +type Bionicle struct { + Name string `json:"name"` +} + +// BionicleName defines model for bionicleName. +type BionicleName = string + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (GET /bionicle/{name}) + GetBionicleName(w http.ResponseWriter, r *http.Request, name BionicleName) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// GetBionicleName operation middleware +func (siw *ServerInterfaceWrapper) GetBionicleName(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "name" ------------- + var name BionicleName + + err = runtime.BindStyledParameterWithOptions("simple", "name", mux.Vars(r)["name"], &name, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "name", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetBionicleName(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{}) +} + +type GorillaServerOptions struct { + BaseURL string + BaseRouter *mux.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = mux.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.HandleFunc(options.BaseURL+"/bionicle/{name}", wrapper.GetBionicleName).Methods("GET") + + return r +} + +type GetBionicleNameRequestObject struct { + Name BionicleName `json:"name"` +} + +type GetBionicleNameResponseObject interface { + VisitGetBionicleNameResponse(w http.ResponseWriter) error +} + +type GetBionicleName200JSONResponse Bionicle + +func (response GetBionicleName200JSONResponse) VisitGetBionicleNameResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetBionicleName400JSONResponse struct { + union json.RawMessage +} + +func (response GetBionicleName400JSONResponse) VisitGetBionicleNameResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response.union) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + + // (GET /bionicle/{name}) + GetBionicleName(ctx context.Context, request GetBionicleNameRequestObject) (GetBionicleNameResponseObject, error) +} + +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc + +type StrictHTTPServerOptions struct { + RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) + ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{ + RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + }, + ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusInternalServerError) + }, + }} +} + +func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc + options StrictHTTPServerOptions +} + +// GetBionicleName operation middleware +func (sh *strictHandler) GetBionicleName(w http.ResponseWriter, r *http.Request, name BionicleName) { + var request GetBionicleNameRequestObject + + request.Name = name + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetBionicleName(ctx, request.(GetBionicleNameRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetBionicleName") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetBionicleNameResponseObject); ok { + if err := validResponse.VisitGetBionicleNameResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/5SRMU8zMQyG/0rl7xujyxWYbuyCWGBhqzqkqa+XqpcEx0VCp/x35FyPXikMZLEcO3n8", + "+h3Ahj4Gj54TNANEQ6ZHRirZ1gXv7BGfTY+S7zBZcpFd8NCA3C5Cu+AOF7YzZCwjgQInxWi4AwW+vByD", + "AsK3kyPcQcN0QgXJdtgb+Zk/ovQlJuf3kHOeimWO1XmOMiGFiMQOS8WfJ/v+fs5aj10bNXWF7QEtjxTn", + "23Ar7RUTgwJ2LNApfUdKY31Z1VUNWUGI6E100MB9VVdLUEV4GU1P29OD8LPc7ZEliAIjqKcdNPCIvJov", + "Wl25sB7gP2ELDfzTF6/0pUVfuZQ3Ij3F4NO4obu6lmCDZ/SFbmI8Olv4+pBEzzBz4ifY2Qr95UMuu3v4", + "49fB40v7q6JbyCZP5zMAAP//9Jg3p6cCAAA=", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + pathPrefix := path.Dir(pathToFile) + + for rawPath, rawFunc := range externalRef0.PathToRawSpec(path.Join(pathPrefix, "common.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/issues/issue-1378/bionicle/config.yaml b/internal/test/issues/issue-1378/bionicle/config.yaml new file mode 100644 index 000000000..21cdcd41e --- /dev/null +++ b/internal/test/issues/issue-1378/bionicle/config.yaml @@ -0,0 +1,9 @@ +package: bionicle +generate: + gorilla-server: true + embedded-spec: true + strict-server: true + models: true +import-mapping: + common.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/common +output: bionicle.gen.go diff --git a/internal/test/issues/issue-1378/bionicle/generate.go b/internal/test/issues/issue-1378/bionicle/generate.go new file mode 100644 index 000000000..86c5c1ab0 --- /dev/null +++ b/internal/test/issues/issue-1378/bionicle/generate.go @@ -0,0 +1,3 @@ +package bionicle + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml ../bionicle.yaml diff --git a/internal/test/issues/issue-1378/common.yaml b/internal/test/issues/issue-1378/common.yaml new file mode 100644 index 000000000..8aac6cdf4 --- /dev/null +++ b/internal/test/issues/issue-1378/common.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.1 + +info: + title: Test + description: Test + version: 1.0.0 + +components: + schemas: + ErrTracingIdNotSent: + type: object + properties: + code: + type: string + required: + - code diff --git a/internal/test/issues/issue-1378/common/common.gen.go b/internal/test/issues/issue-1378/common/common.gen.go new file mode 100644 index 000000000..ce5ae4acf --- /dev/null +++ b/internal/test/issues/issue-1378/common/common.gen.go @@ -0,0 +1,262 @@ +// Package common provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package common + +import ( + "bytes" + "compress/gzip" + "encoding/base64" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/gorilla/mux" + strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" +) + +// ErrTracingIdNotSent defines model for ErrTracingIdNotSent. +type ErrTracingIdNotSent struct { + Code string `json:"code"` +} + +// ServerInterface represents all server handlers. +type ServerInterface interface { +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{}) +} + +type GorillaServerOptions struct { + BaseURL string + BaseRouter *mux.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = mux.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + + return r +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { +} + +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc + +type StrictHTTPServerOptions struct { + RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) + ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{ + RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + }, + ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusInternalServerError) + }, + }} +} + +func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc + options StrictHTTPServerOptions +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/0TMP08DMQwF8O/y5uh0FVt2BhYWuiGGkJjW6Gobx0VCVb47Sk+IyX/eT++GqhdTIYmO", + "fEOvZ7qU+/rofvRSWU5P7VnjhSTm21yNPJjuqGqjOePHCBk9nOWEMRKcvq7s1JBfd/WW/pS+f1INjMlY", + "PnQWNOrV2YJVkHGkHkgIjo3+z2/yvueHZV1WjAQ1kmKMjIdlXQ5IsBLnjizXbRu/AQAA//9/cprb3gAA", + "AA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/issues/issue-1378/common/config.yaml b/internal/test/issues/issue-1378/common/config.yaml new file mode 100644 index 000000000..82fd8a275 --- /dev/null +++ b/internal/test/issues/issue-1378/common/config.yaml @@ -0,0 +1,9 @@ +package: common +generate: + gorilla-server: true + embedded-spec: true + strict-server: true + models: true +output-options: + skip-prune: true +output: common.gen.go diff --git a/internal/test/issues/issue-1378/common/generate.go b/internal/test/issues/issue-1378/common/generate.go new file mode 100644 index 000000000..b54279ec4 --- /dev/null +++ b/internal/test/issues/issue-1378/common/generate.go @@ -0,0 +1,3 @@ +package common + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml ../common.yaml diff --git a/internal/test/issues/issue-1378/foo-service.yaml b/internal/test/issues/issue-1378/foo-service.yaml new file mode 100644 index 000000000..ebd844744 --- /dev/null +++ b/internal/test/issues/issue-1378/foo-service.yaml @@ -0,0 +1,10 @@ +openapi: 3.0.1 + +info: + title: Test + description: Test + version: 1.0.0 + +paths: + /bionicle/{name}: + $ref: "bionicle.yaml#/paths/~1bionicle~1{name}" diff --git a/internal/test/issues/issue-1378/fooservice/config.yaml b/internal/test/issues/issue-1378/fooservice/config.yaml new file mode 100644 index 000000000..df64cfa03 --- /dev/null +++ b/internal/test/issues/issue-1378/fooservice/config.yaml @@ -0,0 +1,10 @@ +package: fooservice +generate: + gorilla-server: true + embedded-spec: true + strict-server: true + models: true +import-mapping: + common.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/common + bionicle.yaml: github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/bionicle +output: fooservice.gen.go diff --git a/internal/test/issues/issue-1378/fooservice/fooservice.gen.go b/internal/test/issues/issue-1378/fooservice/fooservice.gen.go new file mode 100644 index 000000000..8d2bb1fc7 --- /dev/null +++ b/internal/test/issues/issue-1378/fooservice/fooservice.gen.go @@ -0,0 +1,371 @@ +// Package fooservice provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package fooservice + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + externalRef0 "github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/bionicle" + externalRef1 "github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1378/common" + "github.com/getkin/kin-openapi/openapi3" + "github.com/gorilla/mux" + "github.com/oapi-codegen/runtime" + strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (GET /bionicle/{name}) + GetBionicleName(w http.ResponseWriter, r *http.Request, name externalRef0.BionicleName) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// GetBionicleName operation middleware +func (siw *ServerInterfaceWrapper) GetBionicleName(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var err error + + // ------------- Path parameter "name" ------------- + var name externalRef0.BionicleName + + err = runtime.BindStyledParameterWithOptions("simple", "name", mux.Vars(r)["name"], &name, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "name", Err: err}) + return + } + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetBionicleName(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError struct { + ParamName string + Err error +} + +func (e *UnmarshalingParamError) Error() string { + return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +} + +func (e *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{}) +} + +type GorillaServerOptions struct { + BaseURL string + BaseRouter *mux.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r *mux.Router) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r *mux.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, GorillaServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = mux.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + r.HandleFunc(options.BaseURL+"/bionicle/{name}", wrapper.GetBionicleName).Methods("GET") + + return r +} + +type GetBionicleNameRequestObject struct { + Name externalRef0.BionicleName `json:"name"` +} + +type GetBionicleNameResponseObject interface { + VisitGetBionicleNameResponse(w http.ResponseWriter) error +} + +type GetBionicleName200JSONResponse externalRef0.Bionicle + +func (response GetBionicleName200JSONResponse) VisitGetBionicleNameResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetBionicleName400JSONResponse struct { + union json.RawMessage +} + +func (response GetBionicleName400JSONResponse) VisitGetBionicleNameResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response.union) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + + // (GET /bionicle/{name}) + GetBionicleName(ctx context.Context, request GetBionicleNameRequestObject) (GetBionicleNameResponseObject, error) +} + +type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc +type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc + +type StrictHTTPServerOptions struct { + RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) + ResponseErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: StrictHTTPServerOptions{ + RequestErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + }, + ResponseErrorHandlerFunc: func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusInternalServerError) + }, + }} +} + +func NewStrictHandlerWithOptions(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc, options StrictHTTPServerOptions) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares, options: options} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc + options StrictHTTPServerOptions +} + +// GetBionicleName operation middleware +func (sh *strictHandler) GetBionicleName(w http.ResponseWriter, r *http.Request, name externalRef0.BionicleName) { + var request GetBionicleNameRequestObject + + request.Name = name + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetBionicleName(ctx, request.(GetBionicleNameRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetBionicleName") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetBionicleNameResponseObject); ok { + if err := validResponse.VisitGetBionicleNameResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/5SRMU8zMQyG/0rl7xujyxWYbuyCWGBhqzqkqa+XqpcEx0VCp/x35FyPXikMZLEcO3n8", + "+h3Ahj4Gj54TNANEQ6ZHRirZ1gXv7BGfTY+S7zBZcpFd8NCA3C5Cu+AOF7YzZCwjgQInxWi4AwW+vByD", + "AsK3kyPcQcN0QgXJdtgb+Zk/ovQlJuf3kHOeimWO1XmOMiGFiMQOS8WfJ/v+fs5aj10bNXWF7QEtjxTn", + "23Ar7RUTgwJ2LNApfUdKY31Z1VUNWUGI6E100MB9VVdLUEV4GU1P29OD8LPc7ZEliAIjqKcdNPCIvJov", + "Wl25sB7gP2ELDfzTF6/0pUVfuZQ3Ij3F4NO4obu6lmCDZ/SFbmI8Olv4+pBEzzBz4ifY2Qr95UMuu3v4", + "49fB40v7q6JbyCZP5zMAAP//9Jg3p6cCAAA=", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + pathPrefix := path.Dir(pathToFile) + + for rawPath, rawFunc := range externalRef0.PathToRawSpec(path.Join(pathPrefix, "bionicle.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + for rawPath, rawFunc := range externalRef1.PathToRawSpec(path.Join(pathPrefix, "common.yaml")) { + if _, ok := res[rawPath]; ok { + // it is not possible to compare functions in golang, so always overwrite the old value + } + res[rawPath] = rawFunc + } + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/internal/test/issues/issue-1378/fooservice/generate.go b/internal/test/issues/issue-1378/fooservice/generate.go new file mode 100644 index 000000000..30826e829 --- /dev/null +++ b/internal/test/issues/issue-1378/fooservice/generate.go @@ -0,0 +1,3 @@ +package fooservice + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=config.yaml ../foo-service.yaml diff --git a/pkg/codegen/externalref.go b/pkg/codegen/externalref.go new file mode 100644 index 000000000..a48b3d4e1 --- /dev/null +++ b/pkg/codegen/externalref.go @@ -0,0 +1,80 @@ +package codegen + +import ( + "fmt" + "strings" +) + +// ensureExternalRefsInRequestBodyDefinitions ensures that when an externalRef (`$ref` that points to a file that isn't the current spec) is encountered, we make sure we update our underlying `RefType` to make sure that we point to that type. +// This only happens if we have a non-empty `ref` passed in, and that `ref` isn't pointing to something in our file +// NOTE that the pointer here allows us to pass in a reference and edit in-place +func ensureExternalRefsInRequestBodyDefinitions(defs *[]RequestBodyDefinition, ref string) { + if ref == "" { + return + } + + for i, rbd := range *defs { + ensureExternalRefsInSchema(&rbd.Schema, ref) + + // make sure we then update it in-place + (*defs)[i] = rbd + } +} + +// ensureExternalRefsInResponseDefinitions ensures that when an externalRef (`$ref` that points to a file that isn't the current spec) is encountered, we make sure we update our underlying `RefType` to make sure that we point to that type. +// This only happens if we have a non-empty `ref` passed in, and that `ref` isn't pointing to something in our file +// NOTE that the pointer here allows us to pass in a reference and edit in-place +func ensureExternalRefsInResponseDefinitions(defs *[]ResponseDefinition, ref string) { + if ref == "" { + return + } + + for i, rd := range *defs { + for j, rcd := range rd.Contents { + ensureExternalRefsInSchema(&rcd.Schema, ref) + + // make sure we then update it in-place + rd.Contents[j] = rcd + } + + // make sure we then update it in-place + (*defs)[i] = rd + } +} + +// ensureExternalRefsInParameterDefinitions ensures that when an externalRef (`$ref` that points to a file that isn't the current spec) is encountered, we make sure we update our underlying `RefType` to make sure that we point to that type. +// This only happens if we have a non-empty `ref` passed in, and that `ref` isn't pointing to something in our file +// NOTE that the pointer here allows us to pass in a reference and edit in-place +func ensureExternalRefsInParameterDefinitions(defs *[]ParameterDefinition, ref string) { + if ref == "" { + return + } + + for i, pd := range *defs { + ensureExternalRefsInSchema(&pd.Schema, ref) + + // make sure we then update it in-place + (*defs)[i] = pd + } +} + +// ensureExternalRefsInSchema ensures that when an externalRef (`$ref` that points to a file that isn't the current spec) is encountered, we make sure we update our underlying `RefType` to make sure that we point to that type. +// +// This only happens if we have a non-empty `ref` passed in, and that `ref` isn't pointing to something in our file +// +// NOTE that the pointer here allows us to pass in a reference and edit in-place +func ensureExternalRefsInSchema(schema *Schema, ref string) { + if ref == "" { + return + } + + // if this is already defined as the start of a struct, we shouldn't inject **??** + if strings.HasPrefix(schema.GoType, "struct {") { + return + } + + parts := strings.SplitN(ref, "#", 2) + if pack, ok := globalState.importMapping[parts[0]]; ok { + schema.RefType = fmt.Sprintf("%s.%s", pack.Name, schema.GoType) + } +} diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go index 950186f6f..ce0fb8afb 100644 --- a/pkg/codegen/operations.go +++ b/pkg/codegen/operations.go @@ -587,6 +587,8 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper return nil, err } + ensureExternalRefsInParameterDefinitions(&allParams, pathItem.Ref) + // Order the path parameters to match the order as specified in // the path, not in the swagger spec, and validate that the parameter // names match, as downstream code depends on that. @@ -601,11 +603,15 @@ func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]Oper return nil, fmt.Errorf("error generating body definitions: %w", err) } + ensureExternalRefsInRequestBodyDefinitions(&bodyDefinitions, pathItem.Ref) + responseDefinitions, err := GenerateResponseDefinitions(op.OperationID, op.Responses.Map()) if err != nil { return nil, fmt.Errorf("error generating response definitions: %w", err) } + ensureExternalRefsInResponseDefinitions(&responseDefinitions, pathItem.Ref) + opDef := OperationDefinition{ PathParams: pathParams, HeaderParams: FilterParameterDefinitionByType(allParams, "header"), @@ -817,6 +823,7 @@ func GenerateResponseDefinitions(operationID string, responses map[string]*opena NameTag: tag, Schema: contentSchema, } + responseContentDefinitions = append(responseContentDefinitions, rcd) }