From b2337fbc274078201541fd89ad5409af6045f4af Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Thu, 9 Jul 2026 14:58:26 -0700 Subject: [PATCH 1/2] fix: bind only the request body in echo v5 strict handlers The echo v5 strict handler template used ctx.Bind, which binds path values (and, for some methods, query parameters) into the request body struct before binding the body itself, so a path parameter sharing a name with a body field would overwrite it. Generated code now type-switches on the binder installed on the Echo instance: with the default binder, only the body is bound via the package-level echo.BindBody; a custom binder is deferred to entirely, since the echo.Binder interface does not expose body-only binding. Strict mode for echo v5 previously had no test coverage, so this also adds internal/test/servers/strict/echo5, mirroring the echo v4 strict server tests against the shared strict-schema spec. This is a follow-up to PR #2063, which fixed the same problem for echo v4, and extends the fix for issue #1757 to echo v5. --- .../test/servers/strict/echo5/server.cfg.yaml | 7 + .../test/servers/strict/echo5/server.gen.go | 1697 +++++++++++++++++ internal/test/servers/strict/echo5/server.go | 156 ++ .../test/servers/strict/echo5/types.cfg.yaml | 5 + .../test/servers/strict/echo5/types.gen.go | 145 ++ internal/test/servers/strict/strict_test.go | 10 + .../templates/strict/strict-echo5.tmpl | 12 +- 7 files changed, 2031 insertions(+), 1 deletion(-) create mode 100644 internal/test/servers/strict/echo5/server.cfg.yaml create mode 100644 internal/test/servers/strict/echo5/server.gen.go create mode 100644 internal/test/servers/strict/echo5/server.go create mode 100644 internal/test/servers/strict/echo5/types.cfg.yaml create mode 100644 internal/test/servers/strict/echo5/types.gen.go diff --git a/internal/test/servers/strict/echo5/server.cfg.yaml b/internal/test/servers/strict/echo5/server.cfg.yaml new file mode 100644 index 0000000000..0d79ecb90a --- /dev/null +++ b/internal/test/servers/strict/echo5/server.cfg.yaml @@ -0,0 +1,7 @@ +# yaml-language-server: $schema=../../../../../configuration-schema.json +package: api +generate: + echo5-server: true + strict-server: true + embedded-spec: true +output: server.gen.go diff --git a/internal/test/servers/strict/echo5/server.gen.go b/internal/test/servers/strict/echo5/server.gen.go new file mode 100644 index 0000000000..a676f7c8f9 --- /dev/null +++ b/internal/test/servers/strict/echo5/server.gen.go @@ -0,0 +1,1697 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package api + +import ( + "bytes" + "compress/flate" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "mime" + "mime/multipart" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v5" + "github.com/oapi-codegen/runtime" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (POST /json) + JSONExample(ctx *echo.Context) error + + // (POST /multipart) + MultipartExample(ctx *echo.Context) error + + // (POST /multipart-related) + MultipartRelatedExample(ctx *echo.Context) error + + // (POST /multiple) + MultipleRequestAndResponseTypes(ctx *echo.Context) error + + // (POST /no-content-headers) + NoContentHeaders(ctx *echo.Context) error + + // (POST /required-json-body) + RequiredJSONBody(ctx *echo.Context) error + + // (POST /required-text-body) + RequiredTextBody(ctx *echo.Context) error + + // (GET /reserved-go-keyword-parameters/{type}) + ReservedGoKeywordParameters(ctx *echo.Context, pType string) error + + // (POST /reusable-responses) + ReusableResponses(ctx *echo.Context) error + + // (POST /text) + TextExample(ctx *echo.Context) error + + // (POST /unknown) + UnknownExample(ctx *echo.Context) error + + // (POST /unspecified-content-type) + UnspecifiedContentType(ctx *echo.Context) error + + // (POST /urlencoded) + URLEncodedExample(ctx *echo.Context) error + + // (POST /with-headers) + HeadersExample(ctx *echo.Context, params HeadersExampleParams) error + + // (POST /with-union) + UnionExample(ctx *echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// JSONExample converts echo context to params. +func (w *ServerInterfaceWrapper) JSONExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.JSONExample(ctx) + return err +} + +// MultipartExample converts echo context to params. +func (w *ServerInterfaceWrapper) MultipartExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.MultipartExample(ctx) + 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 + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.MultipleRequestAndResponseTypes(ctx) + return err +} + +// NoContentHeaders converts echo context to params. +func (w *ServerInterfaceWrapper) NoContentHeaders(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.NoContentHeaders(ctx) + return err +} + +// RequiredJSONBody converts echo context to params. +func (w *ServerInterfaceWrapper) RequiredJSONBody(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.RequiredJSONBody(ctx) + return err +} + +// RequiredTextBody converts echo context to params. +func (w *ServerInterfaceWrapper) RequiredTextBody(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.RequiredTextBody(ctx) + return err +} + +// ReservedGoKeywordParameters converts echo context to params. +func (w *ServerInterfaceWrapper) ReservedGoKeywordParameters(ctx *echo.Context) error { + var err error + // ------------- Path parameter "type" ------------- + var pType string + + err = runtime.BindStyledParameterWithOptions("simple", "type", ctx.Param("type"), &pType, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter type: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.ReservedGoKeywordParameters(ctx, pType) + return err +} + +// ReusableResponses converts echo context to params. +func (w *ServerInterfaceWrapper) ReusableResponses(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.ReusableResponses(ctx) + return err +} + +// TextExample converts echo context to params. +func (w *ServerInterfaceWrapper) TextExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.TextExample(ctx) + return err +} + +// UnknownExample converts echo context to params. +func (w *ServerInterfaceWrapper) UnknownExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UnknownExample(ctx) + return err +} + +// UnspecifiedContentType converts echo context to params. +func (w *ServerInterfaceWrapper) UnspecifiedContentType(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UnspecifiedContentType(ctx) + return err +} + +// URLEncodedExample converts echo context to params. +func (w *ServerInterfaceWrapper) URLEncodedExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.URLEncodedExample(ctx) + return err +} + +// HeadersExample converts echo context to params. +func (w *ServerInterfaceWrapper) HeadersExample(ctx *echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params HeadersExampleParams + + headers := ctx.Request().Header + // ------------- Required header parameter "header1" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("header1")]; found { + var Header1 string + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for header1, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "header1", valueList[0], &Header1, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter header1: %s", err)) + } + + params.Header1 = Header1 + } else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter header1 is required, but not found")) + } + // ------------- Optional header parameter "header2" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("header2")]; found { + var Header2 int + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for header2, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "header2", valueList[0], &Header2, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: false, Type: "integer", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter header2: %s", err)) + } + + params.Header2 = &Header2 + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.HeadersExample(ctx, params) + return err +} + +// UnionExample converts echo context to params. +func (w *ServerInterfaceWrapper) UnionExample(ctx *echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UnionExample(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.RouteInfo + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) echo.RouteInfo +} + +// RegisterHandlersOptions configures RegisterHandlersWithOptions. +type RegisterHandlersOptions struct { + // BaseURL is prepended to every registered path so the API can be served + // under a prefix. + BaseURL string + // OperationMiddlewares lets the caller attach per-operation middleware at + // registration time. The map key is the OpenAPI `operationId` value as it + // appears in the spec (the raw, un-normalized form). Operations that have + // no entry are registered with no extra middleware. A nil map disables + // per-operation middleware entirely. + OperationMiddlewares map[string][]echo.MiddlewareFunc +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{}) +} + +// RegisterHandlersWithBaseURL registers handlers and prepends BaseURL to the +// paths so the API can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + RegisterHandlersWithOptions(router, si, RegisterHandlersOptions{BaseURL: baseURL}) +} + +// RegisterHandlersWithOptions registers handlers using the supplied options, +// including any per-operation middleware. +func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options RegisterHandlersOptions) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.POST(options.BaseURL+"/json", wrapper.JSONExample, options.OperationMiddlewares["JSONExample"]...) + router.POST(options.BaseURL+"/multipart", wrapper.MultipartExample, options.OperationMiddlewares["MultipartExample"]...) + router.POST(options.BaseURL+"/multipart-related", wrapper.MultipartRelatedExample, options.OperationMiddlewares["MultipartRelatedExample"]...) + router.POST(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes, options.OperationMiddlewares["MultipleRequestAndResponseTypes"]...) + router.POST(options.BaseURL+"/no-content-headers", wrapper.NoContentHeaders, options.OperationMiddlewares["NoContentHeaders"]...) + router.POST(options.BaseURL+"/required-json-body", wrapper.RequiredJSONBody, options.OperationMiddlewares["RequiredJSONBody"]...) + router.POST(options.BaseURL+"/required-text-body", wrapper.RequiredTextBody, options.OperationMiddlewares["RequiredTextBody"]...) + router.GET(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters, options.OperationMiddlewares["ReservedGoKeywordParameters"]...) + router.POST(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses, options.OperationMiddlewares["ReusableResponses"]...) + router.POST(options.BaseURL+"/text", wrapper.TextExample, options.OperationMiddlewares["TextExample"]...) + router.POST(options.BaseURL+"/unknown", wrapper.UnknownExample, options.OperationMiddlewares["UnknownExample"]...) + router.POST(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType, options.OperationMiddlewares["UnspecifiedContentType"]...) + router.POST(options.BaseURL+"/urlencoded", wrapper.URLEncodedExample, options.OperationMiddlewares["URLEncodedExample"]...) + router.POST(options.BaseURL+"/with-headers", wrapper.HeadersExample, options.OperationMiddlewares["HeadersExample"]...) + router.POST(options.BaseURL+"/with-union", wrapper.UnionExample, options.OperationMiddlewares["UnionExample"]...) + +} + +type BadrequestResponse struct { +} + +type ReusableresponseResponseHeaders struct { + Header1 string + Header2 int +} +type ReusableresponseJSONResponse struct { + Body Example + + Headers ReusableresponseResponseHeaders +} + +type JSONExampleRequestObject struct { + Body *JSONExampleJSONRequestBody +} + +type JSONExampleResponseObject interface { + VisitJSONExampleResponse(w http.ResponseWriter) error +} + +type JSONExample200JSONResponse Example + +func (response JSONExample200JSONResponse) VisitJSONExampleResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type JSONExample400Response = BadrequestResponse + +func (response JSONExample400Response) VisitJSONExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type JSONExampledefaultResponse struct { + StatusCode int +} + +func (response JSONExampledefaultResponse) VisitJSONExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type MultipartExampleRequestObject struct { + Body *multipart.Reader +} + +type MultipartExampleResponseObject interface { + VisitMultipartExampleResponse(w http.ResponseWriter) error +} + +type MultipartExample200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipartExample200MultipartResponse) VisitMultipartExampleResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + + w.Header().Set("Content-Type", writer.FormDataContentType()) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipartExample400Response = BadrequestResponse + +func (response MultipartExample400Response) VisitMultipartExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type MultipartExampledefaultResponse struct { + StatusCode int +} + +func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + 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 + Body io.Reader + MultipartBody *multipart.Reader + TextBody *MultipleRequestAndResponseTypesTextRequestBody +} + +type MultipleRequestAndResponseTypesResponseObject interface { + VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error +} + +type MultipleRequestAndResponseTypes200JSONResponse Example + +func (response MultipleRequestAndResponseTypes200JSONResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type MultipleRequestAndResponseTypes200FormdataResponse Example + +func (response MultipleRequestAndResponseTypes200FormdataResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + + form, err := runtime.MarshalForm(response, nil) + if err != nil { + return err + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.WriteHeader(200) + _, err = w.Write([]byte(form.Encode())) + return err +} + +type MultipleRequestAndResponseTypes200ImagepngResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response MultipleRequestAndResponseTypes200ImagepngResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "image/png") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type MultipleRequestAndResponseTypes200MultipartResponse func(writer *multipart.Writer) error + +func (response MultipleRequestAndResponseTypes200MultipartResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + writer := multipart.NewWriter(w) + + w.Header().Set("Content-Type", writer.FormDataContentType()) + w.WriteHeader(200) + + defer writer.Close() + return response(writer) +} + +type MultipleRequestAndResponseTypes200TextResponse string + +func (response MultipleRequestAndResponseTypes200TextResponse) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(200) + + _, err := w.Write([]byte(response)) + return err +} + +type MultipleRequestAndResponseTypes400Response = BadrequestResponse + +func (response MultipleRequestAndResponseTypes400Response) VisitMultipleRequestAndResponseTypesResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type NoContentHeadersRequestObject struct { +} + +type NoContentHeadersResponseObject interface { + VisitNoContentHeadersResponse(w http.ResponseWriter) error +} + +type NoContentHeaders204ResponseHeaders struct { + NullableHeader *string + OptionalHeader *string +} + +type NoContentHeaders204Response struct { + Headers NoContentHeaders204ResponseHeaders +} + +func (response NoContentHeaders204Response) VisitNoContentHeadersResponse(w http.ResponseWriter) error { + if response.Headers.NullableHeader != nil { + w.Header().Set("nullable-header", fmt.Sprint(*response.Headers.NullableHeader)) + } + if response.Headers.OptionalHeader != nil { + w.Header().Set("optional-header", fmt.Sprint(*response.Headers.OptionalHeader)) + } + w.WriteHeader(204) + return nil +} + +type RequiredJSONBodyRequestObject struct { + Body *RequiredJSONBodyJSONRequestBody +} + +type RequiredJSONBodyResponseObject interface { + VisitRequiredJSONBodyResponse(w http.ResponseWriter) error +} + +type RequiredJSONBody200JSONResponse Example + +func (response RequiredJSONBody200JSONResponse) VisitRequiredJSONBodyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type RequiredJSONBody400Response = BadrequestResponse + +func (response RequiredJSONBody400Response) VisitRequiredJSONBodyResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type RequiredJSONBodydefaultResponse struct { + StatusCode int +} + +func (response RequiredJSONBodydefaultResponse) VisitRequiredJSONBodyResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type RequiredTextBodyRequestObject struct { + Body *RequiredTextBodyTextRequestBody +} + +type RequiredTextBodyResponseObject interface { + VisitRequiredTextBodyResponse(w http.ResponseWriter) error +} + +type RequiredTextBody200TextResponse string + +func (response RequiredTextBody200TextResponse) VisitRequiredTextBodyResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(200) + + _, err := w.Write([]byte(response)) + return err +} + +type RequiredTextBody400Response = BadrequestResponse + +func (response RequiredTextBody400Response) VisitRequiredTextBodyResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type RequiredTextBodydefaultResponse struct { + StatusCode int +} + +func (response RequiredTextBodydefaultResponse) VisitRequiredTextBodyResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type ReservedGoKeywordParametersRequestObject struct { + Type string `json:"type"` +} + +type ReservedGoKeywordParametersResponseObject interface { + VisitReservedGoKeywordParametersResponse(w http.ResponseWriter) error +} + +type ReservedGoKeywordParameters200TextResponse string + +func (response ReservedGoKeywordParameters200TextResponse) VisitReservedGoKeywordParametersResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(200) + + _, err := w.Write([]byte(response)) + return err +} + +type ReusableResponsesRequestObject struct { + Body *ReusableResponsesJSONRequestBody +} + +type ReusableResponsesResponseObject interface { + VisitReusableResponsesResponse(w http.ResponseWriter) error +} + +type ReusableResponses200JSONResponse struct{ ReusableresponseJSONResponse } + +func (response ReusableResponses200JSONResponse) VisitReusableResponsesResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response.Body); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("header1", fmt.Sprint(response.Headers.Header1)) + w.Header().Set("header2", fmt.Sprint(response.Headers.Header2)) + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type ReusableResponses400Response = BadrequestResponse + +func (response ReusableResponses400Response) VisitReusableResponsesResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type ReusableResponsesdefaultResponse struct { + StatusCode int +} + +func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type TextExampleRequestObject struct { + Body *TextExampleTextRequestBody +} + +type TextExampleResponseObject interface { + VisitTextExampleResponse(w http.ResponseWriter) error +} + +type TextExample200TextResponse string + +func (response TextExample200TextResponse) VisitTextExampleResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "text/plain") + w.WriteHeader(200) + + _, err := w.Write([]byte(response)) + return err +} + +type TextExample400Response = BadrequestResponse + +func (response TextExample400Response) VisitTextExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type TextExampledefaultResponse struct { + StatusCode int +} + +func (response TextExampledefaultResponse) VisitTextExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type UnknownExampleRequestObject struct { + Body io.Reader +} + +type UnknownExampleResponseObject interface { + VisitUnknownExampleResponse(w http.ResponseWriter) error +} + +type UnknownExample200Videomp4Response struct { + Body io.Reader + ContentLength int64 +} + +func (response UnknownExample200Videomp4Response) VisitUnknownExampleResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", "video/mp4") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type UnknownExample400Response = BadrequestResponse + +func (response UnknownExample400Response) VisitUnknownExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type UnknownExampledefaultResponse struct { + StatusCode int +} + +func (response UnknownExampledefaultResponse) VisitUnknownExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type UnspecifiedContentTypeRequestObject struct { + ContentType string + Body io.Reader +} + +type UnspecifiedContentTypeResponseObject interface { + VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error +} + +type UnspecifiedContentType200VideoResponse struct { + Body io.Reader + ContentType string + ContentLength int64 +} + +func (response UnspecifiedContentType200VideoResponse) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error { + + w.Header().Set("Content-Type", response.ContentType) + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type UnspecifiedContentType400Response = BadrequestResponse + +func (response UnspecifiedContentType400Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type UnspecifiedContentType401Response struct { +} + +func (response UnspecifiedContentType401Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error { + w.WriteHeader(401) + return nil +} + +type UnspecifiedContentType403Response struct { +} + +func (response UnspecifiedContentType403Response) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error { + w.WriteHeader(403) + return nil +} + +type UnspecifiedContentTypedefaultResponse struct { + StatusCode int +} + +func (response UnspecifiedContentTypedefaultResponse) VisitUnspecifiedContentTypeResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type URLEncodedExampleRequestObject struct { + Body *URLEncodedExampleFormdataRequestBody +} + +type URLEncodedExampleResponseObject interface { + VisitURLEncodedExampleResponse(w http.ResponseWriter) error +} + +type URLEncodedExample200FormdataResponse Example + +func (response URLEncodedExample200FormdataResponse) VisitURLEncodedExampleResponse(w http.ResponseWriter) error { + + form, err := runtime.MarshalForm(response, nil) + if err != nil { + return err + } + w.Header().Set("Content-Type", "application/x-www-form-urlencoded") + w.WriteHeader(200) + _, err = w.Write([]byte(form.Encode())) + return err +} + +type URLEncodedExample400Response = BadrequestResponse + +func (response URLEncodedExample400Response) VisitURLEncodedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type URLEncodedExampledefaultResponse struct { + StatusCode int +} + +func (response URLEncodedExampledefaultResponse) VisitURLEncodedExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type HeadersExampleRequestObject struct { + Params HeadersExampleParams + Body *HeadersExampleJSONRequestBody +} + +type HeadersExampleResponseObject interface { + VisitHeadersExampleResponse(w http.ResponseWriter) error +} + +type HeadersExample200ResponseHeaders struct { + Header1 string + Header2 int + NullableHeader *string + OptionalHeader *string +} + +type HeadersExample200JSONResponse struct { + Body Example + Headers HeadersExample200ResponseHeaders +} + +func (response HeadersExample200JSONResponse) VisitHeadersExampleResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response.Body); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("header1", fmt.Sprint(response.Headers.Header1)) + w.Header().Set("header2", fmt.Sprint(response.Headers.Header2)) + if response.Headers.NullableHeader != nil { + w.Header().Set("nullable-header", fmt.Sprint(*response.Headers.NullableHeader)) + } + if response.Headers.OptionalHeader != nil { + w.Header().Set("optional-header", fmt.Sprint(*response.Headers.OptionalHeader)) + } + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type HeadersExample400Response = BadrequestResponse + +func (response HeadersExample400Response) VisitHeadersExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type HeadersExampledefaultResponse struct { + StatusCode int +} + +func (response HeadersExampledefaultResponse) VisitHeadersExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +type UnionExampleRequestObject struct { + Body *UnionExampleJSONRequestBody +} + +type UnionExampleResponseObject interface { + VisitUnionExampleResponse(w http.ResponseWriter) error +} + +type UnionExample200ResponseHeaders struct { + Header1 string + Header2 int +} + +type UnionExample200ApplicationAlternativePlusJSONResponse struct { + Body Example + Headers UnionExample200ResponseHeaders +} + +func (response UnionExample200ApplicationAlternativePlusJSONResponse) VisitUnionExampleResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response.Body); err != nil { + return err + } + w.Header().Set("Content-Type", "application/alternative+json") + w.Header().Set("header1", fmt.Sprint(response.Headers.Header1)) + w.Header().Set("header2", fmt.Sprint(response.Headers.Header2)) + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type UnionExample200JSONResponse struct { + Body UnionExample200JSONResponseBody + Headers UnionExample200ResponseHeaders +} + +func (response UnionExample200JSONResponse) VisitUnionExampleResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response.Body.union); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("header1", fmt.Sprint(response.Headers.Header1)) + w.Header().Set("header2", fmt.Sprint(response.Headers.Header2)) + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + +type UnionExample400Response = BadrequestResponse + +func (response UnionExample400Response) VisitUnionExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(400) + return nil +} + +type UnionExampledefaultResponse struct { + StatusCode int +} + +func (response UnionExampledefaultResponse) VisitUnionExampleResponse(w http.ResponseWriter) error { + w.WriteHeader(response.StatusCode) + return nil +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + + // (POST /json) + JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error) + + // (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) + + // (POST /no-content-headers) + NoContentHeaders(ctx context.Context, request NoContentHeadersRequestObject) (NoContentHeadersResponseObject, error) + + // (POST /required-json-body) + RequiredJSONBody(ctx context.Context, request RequiredJSONBodyRequestObject) (RequiredJSONBodyResponseObject, error) + + // (POST /required-text-body) + RequiredTextBody(ctx context.Context, request RequiredTextBodyRequestObject) (RequiredTextBodyResponseObject, error) + + // (GET /reserved-go-keyword-parameters/{type}) + ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error) + + // (POST /reusable-responses) + ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + + // (POST /text) + TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) + + // (POST /unknown) + UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error) + + // (POST /unspecified-content-type) + UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error) + + // (POST /urlencoded) + URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error) + + // (POST /with-headers) + HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error) + + // (POST /with-union) + UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) +} + +type StrictHandlerFunc func(ctx *echo.Context, request any) (any, error) +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// JSONExample operation middleware +func (sh *strictHandler) JSONExample(ctx *echo.Context) error { + var request JSONExampleRequestObject + + var body JSONExampleJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.JSONExample(ctx.Request().Context(), request.(JSONExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "JSONExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(JSONExampleResponseObject); ok { + return validResponse.VisitJSONExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// MultipartExample operation middleware +func (sh *strictHandler) MultipartExample(ctx *echo.Context) error { + var request MultipartExampleRequestObject + + if reader, err := ctx.Request().MultipartReader(); err != nil { + return err + } else { + request.Body = reader + } + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.MultipartExample(ctx.Request().Context(), request.(MultipartExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipartExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(MultipartExampleResponseObject); ok { + return validResponse.VisitMultipartExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + 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 + + if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "application/json") { + var body MultipleRequestAndResponseTypesJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.JSONBody = &body + } + if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "application/x-www-form-urlencoded") { + if form, err := ctx.FormValues(); err == nil { + var body MultipleRequestAndResponseTypesFormdataRequestBody + if err := runtime.BindForm(&body, form, nil, nil); err != nil { + return err + } + request.FormdataBody = &body + } else { + return err + } + } + if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "image/png") { + request.Body = ctx.Request().Body + } + if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "multipart/form-data") { + if reader, err := ctx.Request().MultipartReader(); err != nil { + return err + } else { + request.MultipartBody = reader + } + } + if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "text/plain") { + data, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + body := MultipleRequestAndResponseTypesTextRequestBody(data) + request.TextBody = &body + } + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.MultipleRequestAndResponseTypes(ctx.Request().Context(), request.(MultipleRequestAndResponseTypesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MultipleRequestAndResponseTypes") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok { + return validResponse.VisitMultipleRequestAndResponseTypesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// NoContentHeaders operation middleware +func (sh *strictHandler) NoContentHeaders(ctx *echo.Context) error { + var request NoContentHeadersRequestObject + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.NoContentHeaders(ctx.Request().Context(), request.(NoContentHeadersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "NoContentHeaders") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(NoContentHeadersResponseObject); ok { + return validResponse.VisitNoContentHeadersResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// RequiredJSONBody operation middleware +func (sh *strictHandler) RequiredJSONBody(ctx *echo.Context) error { + var request RequiredJSONBodyRequestObject + + var body RequiredJSONBodyJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.RequiredJSONBody(ctx.Request().Context(), request.(RequiredJSONBodyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "RequiredJSONBody") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(RequiredJSONBodyResponseObject); ok { + return validResponse.VisitRequiredJSONBodyResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// RequiredTextBody operation middleware +func (sh *strictHandler) RequiredTextBody(ctx *echo.Context) error { + var request RequiredTextBodyRequestObject + + data, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + body := RequiredTextBodyTextRequestBody(data) + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.RequiredTextBody(ctx.Request().Context(), request.(RequiredTextBodyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "RequiredTextBody") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(RequiredTextBodyResponseObject); ok { + return validResponse.VisitRequiredTextBodyResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// ReservedGoKeywordParameters operation middleware +func (sh *strictHandler) ReservedGoKeywordParameters(ctx *echo.Context, pType string) error { + var request ReservedGoKeywordParametersRequestObject + + request.Type = pType + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.ReservedGoKeywordParameters(ctx.Request().Context(), request.(ReservedGoKeywordParametersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "ReservedGoKeywordParameters") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok { + return validResponse.VisitReservedGoKeywordParametersResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// ReusableResponses operation middleware +func (sh *strictHandler) ReusableResponses(ctx *echo.Context) error { + var request ReusableResponsesRequestObject + + var body ReusableResponsesJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.ReusableResponses(ctx.Request().Context(), request.(ReusableResponsesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "ReusableResponses") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(ReusableResponsesResponseObject); ok { + return validResponse.VisitReusableResponsesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// TextExample operation middleware +func (sh *strictHandler) TextExample(ctx *echo.Context) error { + var request TextExampleRequestObject + + data, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + body := TextExampleTextRequestBody(data) + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.TextExample(ctx.Request().Context(), request.(TextExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "TextExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(TextExampleResponseObject); ok { + return validResponse.VisitTextExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// UnknownExample operation middleware +func (sh *strictHandler) UnknownExample(ctx *echo.Context) error { + var request UnknownExampleRequestObject + + request.Body = ctx.Request().Body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.UnknownExample(ctx.Request().Context(), request.(UnknownExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UnknownExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(UnknownExampleResponseObject); ok { + return validResponse.VisitUnknownExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// UnspecifiedContentType operation middleware +func (sh *strictHandler) UnspecifiedContentType(ctx *echo.Context) error { + var request UnspecifiedContentTypeRequestObject + + request.ContentType = ctx.Request().Header.Get("Content-Type") + + request.Body = ctx.Request().Body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.UnspecifiedContentType(ctx.Request().Context(), request.(UnspecifiedContentTypeRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UnspecifiedContentType") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok { + return validResponse.VisitUnspecifiedContentTypeResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// URLEncodedExample operation middleware +func (sh *strictHandler) URLEncodedExample(ctx *echo.Context) error { + var request URLEncodedExampleRequestObject + + if form, err := ctx.FormValues(); err == nil { + var body URLEncodedExampleFormdataRequestBody + if err := runtime.BindForm(&body, form, nil, nil); err != nil { + return err + } + request.Body = &body + } else { + return err + } + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.URLEncodedExample(ctx.Request().Context(), request.(URLEncodedExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "URLEncodedExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok { + return validResponse.VisitURLEncodedExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// HeadersExample operation middleware +func (sh *strictHandler) HeadersExample(ctx *echo.Context, params HeadersExampleParams) error { + var request HeadersExampleRequestObject + + request.Params = params + + var body HeadersExampleJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.HeadersExample(ctx.Request().Context(), request.(HeadersExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "HeadersExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(HeadersExampleResponseObject); ok { + return validResponse.VisitHeadersExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// UnionExample operation middleware +func (sh *strictHandler) UnionExample(ctx *echo.Context) error { + var request UnionExampleRequestObject + + var body UnionExampleJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + return err + } + request.Body = &body + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.UnionExample(ctx.Request().Context(), request.(UnionExampleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UnionExample") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(UnionExampleResponseObject); ok { + return validResponse.VisitUnionExampleResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// Base64 encoded, compressed with deflate, json marshaled OpenAPI spec. +// Stored as a slice of fixed-width chunks rather than one concatenated +// const string: with thousands of chunks the chained `+` fold is several +// times slower for the Go compiler than parsing a slice literal. +var swaggerSpec = []string{ + "7Fnbcts2E36VHfz/RZuSpuL4SndNJpO2aZOObF91fAERKwkJCSDAUrJGo5k+RJ+wT9IBAepI21KqgyfT", + "O4ncE/fbXX5LzFiuS6MVKnKsO2MWndHKYf2nz4XFLxU68v8EutxKQ1Ir1mWvuejFe/OEWawc7xfYqHv5", + "XCtCVatyYwqZc6+afXJef8ZcPsKS+1//tzhgXfa/bBlKFu66DO95aQpk8/k82Yjg43uWsBFygbaONvx8", + "GZ7iSyUtCtYlW2Gy4oumBlmXObJSDZk3GtQud1KTinCI1kfjVWOQXqCJsztjxmqDlmTI4ZgXFbZ7jld0", + "/xPmFJ5QqoHezvUbrYhL5UDIwQAtKoKYXPA2HLjKGG0JBfSn4D3kBA7tGC1LGEnygbHr1esQA3YsYWO0", + "Ljh6edG56Hg8tUHFjWRd9qq+lDDDaVQ/0AJAo9vq4pfrjx9AOuAV6ZKTzHlRTKHk1o14gQKkIu1DrHJy", + "F6z2ZOvC+FlE7bcxlQmLxfdai+kxCqqu25Vyv+x0TlS384RdBWdtNhZBZSsNWJsZ8Kpoyfmt+qz0RAFa", + "q218sqysCpKGW1rFaj3bvzUiu6R8YS8baFumghM/UtYP5enciU8tFpz8OHkSgF6Q3A+HFfNHReHf+Dkr", + "BnEet86p65GeOBjpCZAGgbyAiaQRNIobA1Yq4OCkGhYITVBJK5gFxtfij0r04rPceBtHn2fJmpX7dDKZ", + "pHUDVbZAlWvxdRAmTJZ8iJlRw3V1b5sT67L+lHzJbr/gDtTICSO8p8wUXG4kZtPliUb6f5k+WGOHdlU6", + "jRClK4SuvXE/LGThu8vO1ffQGA4NrGs5XgBXAlRVFJ6WLmWiefj7z78A79Hm0qEDGnGCSjmkhQC3CLqU", + "5EnVwOoSaLQ0s9X7H/SbENNPMfytOrxqexKIWutEtok65mIdiOZmw1G3a6HJQKv6dscEBBrqm/qeSPtx", + "QrUjEAcceClP9RrdBJyGUjo/J8NNN9JVIYAXEz51YUJvc75eVPfcrx6NRyd+yQbT/7aJ4AJa39vngfYG", + "7+lJaPcYPfvCd+qptj9E9VYm0qFOP+N0oq1IDbe8RELrspmPc+5tDbHF5O8LSci5gj6C4iUK4ANCC+80", + "RJOuBZ/g951+H0SWpuqVb/Gn+8eM+eTVayBLmHfAuiF/yR7r9t1xoWqyGT5GpGuuHir4KNKkzuLAeUrY", + "hnFL/oKn3orEeZbWx2tz6/PMKYraI/nw6uNHwi7rzgGp33OfAlW4+HDOotYuaftKJrlDFsdSoM5Kc7Wn", + "5bMl1RnM5UCiWHDMENtDI+GNVrlFWl8B/ctQaYKFMehPa0oYMlC/HycIZeUIDHcOJNVTpJDha53Y5oy3", + "y8giDbxZjtPHUH1xJExfnAvRq87L/VVeHblu1la5B/qx9+vbILPvN8uD7Yx7bryH83umdvY73tM7YtzC", + "lu/0HOXYMyIlwCJVVqGAseTNh+it3owGlrC2caG4Xy3YUHMAsQ8hSh61dckePYS4+4Y/kZ/vaCc58QJ+", + "qq6plHzs4ObW34bI6DffVFKrZ3oswwtCqzjJMf5wmO9521a0wo+Duu830Et29HD3/I4vj11184SFk8Yw", + "MCtb+KlGZLpZFk4oL9yED4doL6TOuJE+S/8EAAD//w==", +} + +// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, +// after base64-decoding and flate-decompressing the embedded blob. +func decodeSpec() ([]byte, error) { + encoded := strings.Join(swaggerSpec, "") + compressed, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr := flate.NewReader(bytes.NewReader(compressed)) + var buf bytes.Buffer + if _, err := buf.ReadFrom(zr); err != nil { + return nil, fmt.Errorf("read flate: %w", err) + } + if err := zr.Close(); err != nil { + return nil, fmt.Errorf("close flate reader: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cache of the decoded OpenAPI 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 +} + +// GetSpec returns the OpenAPI specification corresponding to the generated +// code in this file. External references in the spec are resolved through +// PathToRawSpec; externally-referenced files must be embedded in their +// corresponding Go packages (via the import-mapping feature). URL-based +// external refs are not supported. +func GetSpec() (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 +} + +// GetSpecJSON returns the raw JSON bytes of the embedded OpenAPI +// specification: decompressed but not unmarshaled. External references +// are not resolved here; the bytes are the spec exactly as embedded by +// codegen. The result is cached at package init time, so repeated calls +// are cheap. +func GetSpecJSON() ([]byte, error) { + return rawSpec() +} + +// GetSwagger returns the OpenAPI specification corresponding to the +// generated code in this file. +// +// Deprecated: GetSwagger predates kin-openapi renaming openapi3.Swagger +// to openapi3.T. Use [GetSpec] instead. This wrapper is retained for +// backwards compatibility. +func GetSwagger() (*openapi3.T, error) { + return GetSpec() +} diff --git a/internal/test/servers/strict/echo5/server.go b/internal/test/servers/strict/echo5/server.go new file mode 100644 index 0000000000..1f4c633fae --- /dev/null +++ b/internal/test/servers/strict/echo5/server.go @@ -0,0 +1,156 @@ +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=server.cfg.yaml ../strict-schema.yaml +//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen --config=types.cfg.yaml ../strict-schema.yaml + +package api + +import ( + "context" + "encoding/json" + "io" + "mime/multipart" +) + +type StrictServer struct { +} + +func (s StrictServer) JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error) { + return JSONExample200JSONResponse(*request.Body), nil +} + +func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) { + return MultipartExample200MultipartResponse(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) 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: + return MultipleRequestAndResponseTypes200ImagepngResponse{Body: request.Body}, nil + case request.JSONBody != nil: + return MultipleRequestAndResponseTypes200JSONResponse(*request.JSONBody), nil + case request.FormdataBody != nil: + return MultipleRequestAndResponseTypes200FormdataResponse(*request.FormdataBody), nil + case request.TextBody != nil: + return MultipleRequestAndResponseTypes200TextResponse(*request.TextBody), nil + case request.MultipartBody != nil: + return MultipleRequestAndResponseTypes200MultipartResponse(func(writer *multipart.Writer) error { + for { + part, err := request.MultipartBody.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 + default: + return MultipleRequestAndResponseTypes400Response{}, nil + } +} + +func (s StrictServer) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) { + return TextExample200TextResponse(*request.Body), nil +} + +func (s StrictServer) UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error) { + return UnknownExample200Videomp4Response{Body: request.Body}, nil +} + +func (s StrictServer) UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error) { + return UnspecifiedContentType200VideoResponse{Body: request.Body, ContentType: request.ContentType}, nil +} + +func (s StrictServer) URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error) { + return URLEncodedExample200FormdataResponse(*request.Body), nil +} + +func (s StrictServer) HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error) { + return HeadersExample200JSONResponse{Body: *request.Body, Headers: HeadersExample200ResponseHeaders{Header1: request.Params.Header1, Header2: *request.Params.Header2}}, nil +} + +func (s StrictServer) NoContentHeaders(ctx context.Context, request NoContentHeadersRequestObject) (NoContentHeadersResponseObject, error) { + return NoContentHeaders204Response{}, nil +} + +func (s StrictServer) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) { + return ReusableResponses200JSONResponse{ReusableresponseJSONResponse: ReusableresponseJSONResponse{Body: *request.Body}}, nil +} + +func (s StrictServer) RequiredJSONBody(ctx context.Context, request RequiredJSONBodyRequestObject) (RequiredJSONBodyResponseObject, error) { + return RequiredJSONBody200JSONResponse(*request.Body), nil +} + +func (s StrictServer) RequiredTextBody(ctx context.Context, request RequiredTextBodyRequestObject) (RequiredTextBodyResponseObject, error) { + return RequiredTextBody200TextResponse(*request.Body), nil +} + +func (s StrictServer) ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error) { + return ReservedGoKeywordParameters200TextResponse(""), nil +} + +func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequestObject) (UnionExampleResponseObject, error) { + union, err := json.Marshal(*request.Body) + if err != nil { + return nil, err + } + + return UnionExample200JSONResponse{ + Body: struct{ union json.RawMessage }{ + union: union, + }, + }, nil +} diff --git a/internal/test/servers/strict/echo5/types.cfg.yaml b/internal/test/servers/strict/echo5/types.cfg.yaml new file mode 100644 index 0000000000..7a914b14e3 --- /dev/null +++ b/internal/test/servers/strict/echo5/types.cfg.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=../../../../../configuration-schema.json +package: api +generate: + models: true +output: types.gen.go diff --git a/internal/test/servers/strict/echo5/types.gen.go b/internal/test/servers/strict/echo5/types.gen.go new file mode 100644 index 0000000000..08ae8548e1 --- /dev/null +++ b/internal/test/servers/strict/echo5/types.gen.go @@ -0,0 +1,145 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT. +package api + +import ( + "encoding/json" + + "github.com/oapi-codegen/runtime" +) + +// Example defines model for example. +type Example struct { + Value *string `json:"value,omitempty"` +} + +// Reusableresponse defines model for reusableresponse. +type Reusableresponse = Example + +// MultipleRequestAndResponseTypesTextBody defines parameters for MultipleRequestAndResponseTypes. +type MultipleRequestAndResponseTypesTextBody = string + +// RequiredTextBodyTextBody defines parameters for RequiredTextBody. +type RequiredTextBodyTextBody = string + +// TextExampleTextBody defines parameters for TextExample. +type TextExampleTextBody = string + +// HeadersExampleParams defines parameters for HeadersExample. +type HeadersExampleParams struct { + Header1 string `json:"header1"` + Header2 *int `json:"header2,omitempty"` +} + +// UnionExample200JSONResponseBody0 defines parameters for UnionExample. +type UnionExample200JSONResponseBody0 = string + +// UnionExample200JSONResponseBody defines parameters for UnionExample. +type UnionExample200JSONResponseBody struct { + union json.RawMessage +} + +// JSONExampleJSONRequestBody defines body for JSONExample for application/json ContentType. +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 + +// MultipleRequestAndResponseTypesFormdataRequestBody defines body for MultipleRequestAndResponseTypes for application/x-www-form-urlencoded ContentType. +type MultipleRequestAndResponseTypesFormdataRequestBody = Example + +// MultipleRequestAndResponseTypesMultipartRequestBody defines body for MultipleRequestAndResponseTypes for multipart/form-data ContentType. +type MultipleRequestAndResponseTypesMultipartRequestBody = Example + +// MultipleRequestAndResponseTypesTextRequestBody defines body for MultipleRequestAndResponseTypes for text/plain ContentType. +type MultipleRequestAndResponseTypesTextRequestBody = MultipleRequestAndResponseTypesTextBody + +// RequiredJSONBodyJSONRequestBody defines body for RequiredJSONBody for application/json ContentType. +type RequiredJSONBodyJSONRequestBody = Example + +// RequiredTextBodyTextRequestBody defines body for RequiredTextBody for text/plain ContentType. +type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody + +// ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. +type ReusableResponsesJSONRequestBody = Example + +// TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. +type TextExampleTextRequestBody = TextExampleTextBody + +// URLEncodedExampleFormdataRequestBody defines body for URLEncodedExample for application/x-www-form-urlencoded ContentType. +type URLEncodedExampleFormdataRequestBody = Example + +// HeadersExampleJSONRequestBody defines body for HeadersExample for application/json ContentType. +type HeadersExampleJSONRequestBody = Example + +// UnionExampleJSONRequestBody defines body for UnionExample for application/json ContentType. +type UnionExampleJSONRequestBody = Example + +// AsUnionExample200JSONResponseBody0 returns the union data inside the UnionExample200JSONResponseBody as a UnionExample200JSONResponseBody0 +func (t UnionExample200JSONResponseBody) AsUnionExample200JSONResponseBody0() (UnionExample200JSONResponseBody0, error) { + var body UnionExample200JSONResponseBody0 + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromUnionExample200JSONResponseBody0 overwrites any union data inside the UnionExample200JSONResponseBody as the provided UnionExample200JSONResponseBody0 +func (t *UnionExample200JSONResponseBody) FromUnionExample200JSONResponseBody0(v UnionExample200JSONResponseBody0) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeUnionExample200JSONResponseBody0 performs a merge with any union data inside the UnionExample200JSONResponseBody, using the provided UnionExample200JSONResponseBody0 +func (t *UnionExample200JSONResponseBody) MergeUnionExample200JSONResponseBody0(v UnionExample200JSONResponseBody0) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsExample returns the union data inside the UnionExample200JSONResponseBody as a Example +func (t UnionExample200JSONResponseBody) AsExample() (Example, error) { + var body Example + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromExample overwrites any union data inside the UnionExample200JSONResponseBody as the provided Example +func (t *UnionExample200JSONResponseBody) FromExample(v Example) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeExample performs a merge with any union data inside the UnionExample200JSONResponseBody, using the provided Example +func (t *UnionExample200JSONResponseBody) MergeExample(v Example) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t UnionExample200JSONResponseBody) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *UnionExample200JSONResponseBody) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} diff --git a/internal/test/servers/strict/strict_test.go b/internal/test/servers/strict/strict_test.go index e6d8ea8779..c4395b0fe8 100644 --- a/internal/test/servers/strict/strict_test.go +++ b/internal/test/servers/strict/strict_test.go @@ -15,11 +15,13 @@ import ( "github.com/go-chi/chi/v5" "github.com/kataras/iris/v12" "github.com/labstack/echo/v4" + echov5 "github.com/labstack/echo/v5" "github.com/stretchr/testify/assert" chiAPI "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/chi" clientAPI "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/client" echoAPI "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/echo" + echo5API "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/echo5" ginAPI "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/gin" irisAPI "github.com/oapi-codegen/oapi-codegen/v2/internal/test/servers/strict/iris" @@ -51,6 +53,14 @@ func TestEchoServer(t *testing.T) { testImpl(t, e) } +func TestEcho5Server(t *testing.T) { + server := echo5API.StrictServer{} + strictHandler := echo5API.NewStrictHandler(server, nil) + e := echov5.New() + echo5API.RegisterHandlers(e, strictHandler) + testImpl(t, e) +} + func TestGinServer(t *testing.T) { server := ginAPI.StrictServer{} strictHandler := ginAPI.NewStrictHandler(server, nil) diff --git a/pkg/codegen/templates/strict/strict-echo5.tmpl b/pkg/codegen/templates/strict/strict-echo5.tmpl index 687dd57a8b..e1c5ca1c3b 100644 --- a/pkg/codegen/templates/strict/strict-echo5.tmpl +++ b/pkg/codegen/templates/strict/strict-echo5.tmpl @@ -34,7 +34,17 @@ type strictHandler struct { {{if $multipleBodies}}if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), {{.ContentType | toGoString}}) { {{end}} {{if .IsJSON -}} var body {{$opid}}{{.NameTag}}RequestBody - if err := ctx.Bind(&body); err != nil { + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { return err } request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body From 9995cca60f863645000c65ca929b5fe2a3d3da25 Mon Sep 17 00:00:00 2001 From: Marcin Romaszewicz Date: Thu, 9 Jul 2026 15:15:22 -0700 Subject: [PATCH 2/2] fix: bring echo v5 optional-body handling to parity with echo v4 and add issue #1757 regression test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two review follow-ups: Add the optional-body guard from the echo v4 strict template to the echo v5 template, so a binding error caused by an empty body (errors.Is(err, io.EOF), which unwraps through echo v5's ErrBadRequest.Wrap) is tolerated when the request body is not required, leaving the body pointer nil instead of failing the request. Add a targeted regression test for issue #1757 to the shared strict server test suite: a new endpoint whose path parameter shares its name with a request body property, exercised by testImpl across every framework. The body property carries a param tag via x-oapi-codegen-extra-tags, which is what makes Echo's full Bind copy a same-named path parameter into the body struct — Echo's binder skips fields without param/query/form tags, so plain generated structs need an extra tag to be affected. --- .../test/servers/strict/chi/server.gen.go | 144 ++++++++++-- internal/test/servers/strict/chi/server.go | 7 + internal/test/servers/strict/chi/types.gen.go | 8 + .../test/servers/strict/client/client.gen.go | 214 ++++++++++++++++++ .../test/servers/strict/echo/server.gen.go | 135 +++++++++-- internal/test/servers/strict/echo/server.go | 7 + .../test/servers/strict/echo/types.gen.go | 8 + .../test/servers/strict/echo5/server.gen.go | 171 +++++++++++--- internal/test/servers/strict/echo5/server.go | 7 + .../test/servers/strict/echo5/types.gen.go | 8 + .../test/servers/strict/fiber/server.gen.go | 136 +++++++++-- internal/test/servers/strict/fiber/server.go | 7 + .../test/servers/strict/fiber/types.gen.go | 8 + .../test/servers/strict/gin/server.gen.go | 136 +++++++++-- internal/test/servers/strict/gin/server.go | 7 + internal/test/servers/strict/gin/types.gen.go | 8 + .../test/servers/strict/gorilla/server.gen.go | 138 +++++++++-- .../test/servers/strict/gorilla/server.go | 7 + .../test/servers/strict/gorilla/types.gen.go | 8 + .../test/servers/strict/iris/server.gen.go | 129 +++++++++-- internal/test/servers/strict/iris/server.go | 7 + .../test/servers/strict/iris/types.gen.go | 8 + .../test/servers/strict/stdhttp/server.gen.go | 137 +++++++++-- .../test/servers/strict/stdhttp/server.go | 7 + .../test/servers/strict/stdhttp/types.gen.go | 8 + .../test/servers/strict/strict-schema.yaml | 33 +++ internal/test/servers/strict/strict_test.go | 10 + .../templates/strict/strict-echo5.tmpl | 9 +- 28 files changed, 1333 insertions(+), 179 deletions(-) diff --git a/internal/test/servers/strict/chi/server.gen.go b/internal/test/servers/strict/chi/server.gen.go index f54a6191ed..aff67e9c59 100644 --- a/internal/test/servers/strict/chi/server.gen.go +++ b/internal/test/servers/strict/chi/server.gen.go @@ -54,6 +54,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(w http.ResponseWriter, r *http.Request) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) + // (POST /text) TextExample(w http.ResponseWriter, r *http.Request) @@ -122,6 +125,11 @@ func (_ Unimplemented) ReusableResponses(w http.ResponseWriter, r *http.Request) w.WriteHeader(http.StatusNotImplemented) } +// (POST /same-name-param-and-body-property/{name}) +func (_ Unimplemented) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) { + w.WriteHeader(http.StatusNotImplemented) +} + // (POST /text) func (_ Unimplemented) TextExample(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) @@ -299,6 +307,32 @@ func (siw *ServerInterfaceWrapper) ReusableResponses(w http.ResponseWriter, r *h handler.ServeHTTP(w, r) } +// SameNameParamAndBodyProperty operation middleware +func (siw *ServerInterfaceWrapper) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", chi.URLParam(r, "name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + 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.SameNameParamAndBodyProperty(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // TextExample operation middleware func (siw *ServerInterfaceWrapper) TextExample(w http.ResponseWriter, r *http.Request) { @@ -573,6 +607,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/same-name-param-and-body-property/{name}", wrapper.SameNameParamAndBodyProperty) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/text", wrapper.TextExample) }) @@ -967,6 +1004,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1300,6 +1360,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1665,6 +1728,42 @@ func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Reques } } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + } else { + request.Body = &body + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx, request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(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)) + } +} + // TextExample operation middleware func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) { var request TextExampleRequestObject @@ -1863,27 +1962,30 @@ func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/chi/server.go b/internal/test/servers/strict/chi/server.go index 1f4c633fae..5a4eb5f2d6 100644 --- a/internal/test/servers/strict/chi/server.go +++ b/internal/test/servers/strict/chi/server.go @@ -154,3 +154,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/chi/types.gen.go b/internal/test/servers/strict/chi/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/chi/types.gen.go +++ b/internal/test/servers/strict/chi/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/client/client.gen.go b/internal/test/servers/strict/client/client.gen.go index 4bbc51bb7a..c126485848 100644 --- a/internal/test/servers/strict/client/client.gen.go +++ b/internal/test/servers/strict/client/client.gen.go @@ -21,6 +21,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -77,6 +82,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody @@ -315,6 +323,20 @@ type ClientInterface interface { // Responses can be refs to components/responses. ReusableResponses(ctx context.Context, body ReusableResponsesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // SameNameParamAndBodyPropertyWithBody performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request, + // with any type of body and a specified content type. + // + // Regression test for issue #1757: a path parameter must not be bound + // into a request body property just because they share a name. + SameNameParamAndBodyPropertyWithBody(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SameNameParamAndBodyProperty performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request. + // Takes a body of the `application/json` content type. + // + // Regression test for issue #1757: a path parameter must not be bound + // into a request body property just because they share a name. + SameNameParamAndBodyProperty(ctx context.Context, name string, body SameNameParamAndBodyPropertyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // TextExampleWithBody performs a POST /text (the `TextExample` operationId) request, // with any type of body and a specified content type. TextExampleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -616,6 +638,40 @@ func (c *Client) ReusableResponses(ctx context.Context, body ReusableResponsesJS return c.Client.Do(req) } +// SameNameParamAndBodyPropertyWithBody performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request, +// with any type of body and a specified content type. +// +// Regression test for issue #1757: a path parameter must not be bound +// into a request body property just because they share a name. +func (c *Client) SameNameParamAndBodyPropertyWithBody(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSameNameParamAndBodyPropertyRequestWithBody(c.Server, name, 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) +} + +// SameNameParamAndBodyProperty performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request. +// Takes a body of the `application/json` content type. +// +// Regression test for issue #1757: a path parameter must not be bound +// into a request body property just because they share a name. +func (c *Client) SameNameParamAndBodyProperty(ctx context.Context, name string, body SameNameParamAndBodyPropertyJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSameNameParamAndBodyPropertyRequest(c.Server, name, 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) +} + // TextExampleWithBody performs a POST /text (the `TextExample` operationId) request, // with any type of body and a specified content type. func (c *Client) TextExampleWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -1107,6 +1163,53 @@ func NewReusableResponsesRequestWithBody(server string, contentType string, body return req, nil } +// NewSameNameParamAndBodyPropertyRequest calls the generic SameNameParamAndBodyProperty builder with application/json body +func NewSameNameParamAndBodyPropertyRequest(server string, name string, body SameNameParamAndBodyPropertyJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewSameNameParamAndBodyPropertyRequestWithBody(server, name, "application/json", bodyReader) +} + +// NewSameNameParamAndBodyPropertyRequestWithBody constructs an http.Request for the SameNameParamAndBodyProperty method, with any body, and a specified content type +func NewSameNameParamAndBodyPropertyRequestWithBody(server string, name string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "name", name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/same-name-param-and-body-property/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewTextExampleRequestWithTextBody calls the generic TextExample builder with text/plain body func NewTextExampleRequestWithTextBody(server string, body TextExampleTextRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -1501,6 +1604,22 @@ type ClientWithResponsesInterface interface { // Responses can be refs to components/responses. ReusableResponsesWithResponse(ctx context.Context, body ReusableResponsesJSONRequestBody, reqEditors ...RequestEditorFn) (*ReusableResponsesResponse, error) + // SameNameParamAndBodyPropertyWithBodyWithResponse performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request, + // with any type of body and a specified content type. + // + // Regression test for issue #1757: a path parameter must not be bound + // into a request body property just because they share a name. + // + // Returns a wrapper object for the known response body format(s). + SameNameParamAndBodyPropertyWithBodyWithResponse(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SameNameParamAndBodyPropertyResponse, error) + + // SameNameParamAndBodyPropertyWithResponse performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request. + // Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). + // + // Regression test for issue #1757: a path parameter must not be bound + // into a request body property just because they share a name. + SameNameParamAndBodyPropertyWithResponse(ctx context.Context, name string, body SameNameParamAndBodyPropertyJSONRequestBody, reqEditors ...RequestEditorFn) (*SameNameParamAndBodyPropertyResponse, error) + // TextExampleWithBodyWithResponse performs a POST /text (the `TextExample` operationId) request, // with any type of body and a specified content type. // @@ -1898,6 +2017,47 @@ func (r ReusableResponsesResponse) ContentType() string { return "" } +type SameNameParamAndBodyPropertyResponse struct { + Body []byte + HTTPResponse *http.Response + // JSON200 the response for an HTTP 200 `application/json` response + JSON200 *SameName +} + +// GetJSON200 returns the response for an HTTP 200 `application/json` response +func (r SameNameParamAndBodyPropertyResponse) GetJSON200() *SameName { + return r.JSON200 +} + +// GetBody returns the raw response body bytes +func (r SameNameParamAndBodyPropertyResponse) GetBody() []byte { + return r.Body +} + +// Status returns HTTPResponse.Status +func (r SameNameParamAndBodyPropertyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SameNameParamAndBodyPropertyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r SameNameParamAndBodyPropertyResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type TextExampleResponse struct { Body []byte HTTPResponse *http.Response @@ -2327,6 +2487,34 @@ func (c *ClientWithResponses) ReusableResponsesWithResponse(ctx context.Context, return ParseReusableResponsesResponse(rsp) } +// SameNameParamAndBodyPropertyWithBodyWithResponse performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request, +// with any type of body and a specified content type. +// +// Regression test for issue #1757: a path parameter must not be bound +// into a request body property just because they share a name. +// +// Returns a wrapper object for the known response body format(s). +func (c *ClientWithResponses) SameNameParamAndBodyPropertyWithBodyWithResponse(ctx context.Context, name string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SameNameParamAndBodyPropertyResponse, error) { + rsp, err := c.SameNameParamAndBodyPropertyWithBody(ctx, name, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSameNameParamAndBodyPropertyResponse(rsp) +} + +// SameNameParamAndBodyPropertyWithResponse performs a POST /same-name-param-and-body-property/{name} (the `SameNameParamAndBodyProperty` operationId) request. +// Takes a body of the `application/json` content type, and returns a wrapper object for the known response body format(s). +// +// Regression test for issue #1757: a path parameter must not be bound +// into a request body property just because they share a name. +func (c *ClientWithResponses) SameNameParamAndBodyPropertyWithResponse(ctx context.Context, name string, body SameNameParamAndBodyPropertyJSONRequestBody, reqEditors ...RequestEditorFn) (*SameNameParamAndBodyPropertyResponse, error) { + rsp, err := c.SameNameParamAndBodyProperty(ctx, name, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSameNameParamAndBodyPropertyResponse(rsp) +} + // TextExampleWithBodyWithResponse performs a POST /text (the `TextExample` operationId) request, // with any type of body and a specified content type. // @@ -2648,6 +2836,32 @@ func ParseReusableResponsesResponse(rsp *http.Response) (*ReusableResponsesRespo return response, nil } +// ParseSameNameParamAndBodyPropertyResponse parses an HTTP response from a SameNameParamAndBodyPropertyWithResponse call +func ParseSameNameParamAndBodyPropertyResponse(rsp *http.Response) (*SameNameParamAndBodyPropertyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SameNameParamAndBodyPropertyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SameName + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + // ParseTextExampleResponse parses an HTTP response from a TextExampleWithResponse call func ParseTextExampleResponse(rsp *http.Response) (*TextExampleResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/internal/test/servers/strict/echo/server.gen.go b/internal/test/servers/strict/echo/server.gen.go index 7cf9c09b35..0e8912f6cf 100644 --- a/internal/test/servers/strict/echo/server.gen.go +++ b/internal/test/servers/strict/echo/server.gen.go @@ -54,6 +54,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx echo.Context) error + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx echo.Context, name string) error + // (POST /text) TextExample(ctx echo.Context) error @@ -166,6 +169,22 @@ func (w *ServerInterfaceWrapper) ReusableResponses(ctx echo.Context) error { return err } +// SameNameParamAndBodyProperty converts echo context to params. +func (w *ServerInterfaceWrapper) SameNameParamAndBodyProperty(ctx echo.Context) error { + var err error + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", ctx.Param("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.SameNameParamAndBodyProperty(ctx, name) + return err +} + // TextExample converts echo context to params. func (w *ServerInterfaceWrapper) TextExample(ctx echo.Context) error { var err error @@ -313,6 +332,7 @@ func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options router.POST(options.BaseURL+"/required-text-body", wrapper.RequiredTextBody, options.OperationMiddlewares["RequiredTextBody"]...) router.GET(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters, options.OperationMiddlewares["ReservedGoKeywordParameters"]...) router.POST(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses, options.OperationMiddlewares["ReusableResponses"]...) + router.POST(options.BaseURL+"/same-name-param-and-body-property/:name", wrapper.SameNameParamAndBodyProperty, options.OperationMiddlewares["SameNameParamAndBodyProperty"]...) router.POST(options.BaseURL+"/text", wrapper.TextExample, options.OperationMiddlewares["TextExample"]...) router.POST(options.BaseURL+"/unknown", wrapper.UnknownExample, options.OperationMiddlewares["UnknownExample"]...) router.POST(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType, options.OperationMiddlewares["UnspecifiedContentType"]...) @@ -694,6 +714,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1027,6 +1070,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1394,6 +1440,50 @@ func (sh *strictHandler) ReusableResponses(ctx echo.Context) error { return nil } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(ctx echo.Context, name string) error { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + var err error + if binder, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = binder.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body + } + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx.Request().Context(), request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + return validResponse.VisitSameNameParamAndBodyPropertyResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + // TextExample operation middleware func (sh *strictHandler) TextExample(ctx echo.Context) error { var request TextExampleRequestObject @@ -1602,27 +1692,30 @@ func (sh *strictHandler) UnionExample(ctx echo.Context) error { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "7Fnbcts2E36VHfz/RZuSpuL4SndNJpO2aZOObF91fAERKwkJCSDAUrJGo5k+RJ+wT9IBAepI21KqgyfT", - "O4ncE/fbXX5LzFiuS6MVKnKsO2MWndHKYf2nz4XFLxU68v8EutxKQ1Ir1mWvuejFe/OEWawc7xfYqHv5", - "XCtCVatyYwqZc6+afXJef8ZcPsKS+1//tzhgXfa/bBlKFu66DO95aQpk8/k82Yjg43uWsBFygbaONvx8", - "GZ7iSyUtCtYlW2Gy4oumBlmXObJSDZk3GtQud1KTinCI1kfjVWOQXqCJsztjxmqDlmTI4ZgXFbZ7jld0", - "/xPmFJ5QqoHezvUbrYhL5UDIwQAtKoKYXPA2HLjKGG0JBfSn4D3kBA7tGC1LGEnygbHr1esQA3YsYWO0", - "Ljh6edG56Hg8tUHFjWRd9qq+lDDDaVQ/0AJAo9vq4pfrjx9AOuAV6ZKTzHlRTKHk1o14gQKkIu1DrHJy", - "F6z2ZOvC+FlE7bcxlQmLxfdai+kxCqqu25Vyv+x0TlS384RdBWdtNhZBZSsNWJsZ8Kpoyfmt+qz0RAFa", - "q218sqysCpKGW1rFaj3bvzUiu6R8YS8baFumghM/UtYP5enciU8tFpz8OHkSgF6Q3A+HFfNHReHf+Dkr", - "BnEet86p65GeOBjpCZAGgbyAiaQRNIobA1Yq4OCkGhYITVBJK5gFxtfij0r04rPceBtHn2fJmpX7dDKZ", - "pHUDVbZAlWvxdRAmTJZ8iJlRw3V1b5sT67L+lHzJbr/gDtTICSO8p8wUXG4kZtPliUb6f5k+WGOHdlU6", - "jRClK4SuvXE/LGThu8vO1ffQGA4NrGs5XgBXAlRVFJ6WLmWiefj7z78A79Hm0qEDGnGCSjmkhQC3CLqU", - "5EnVwOoSaLQ0s9X7H/SbENNPMfytOrxqexKIWutEtok65mIdiOZmw1G3a6HJQKv6dscEBBrqm/qeSPtx", - "QrUjEAcceClP9RrdBJyGUjo/J8NNN9JVIYAXEz51YUJvc75eVPfcrx6NRyd+yQbT/7aJ4AJa39vngfYG", - "7+lJaPcYPfvCd+qptj9E9VYm0qFOP+N0oq1IDbe8RELrspmPc+5tDbHF5O8LSci5gj6C4iUK4ANCC+80", - "RJOuBZ/g951+H0SWpuqVb/Gn+8eM+eTVayBLmHfAuiF/yR7r9t1xoWqyGT5GpGuuHir4KNKkzuLAeUrY", - "hnFL/oKn3orEeZbWx2tz6/PMKYraI/nw6uNHwi7rzgGp33OfAlW4+HDOotYuaftKJrlDFsdSoM5Kc7Wn", - "5bMl1RnM5UCiWHDMENtDI+GNVrlFWl8B/ctQaYKFMehPa0oYMlC/HycIZeUIDHcOJNVTpJDha53Y5oy3", - "y8giDbxZjtPHUH1xJExfnAvRq87L/VVeHblu1la5B/qx9+vbILPvN8uD7Yx7bryH83umdvY73tM7YtzC", - "lu/0HOXYMyIlwCJVVqGAseTNh+it3owGlrC2caG4Xy3YUHMAsQ8hSh61dckePYS4+4Y/kZ/vaCc58QJ+", - "qq6plHzs4ObW34bI6DffVFKrZ3oswwtCqzjJMf5wmO9521a0wo+Duu830Et29HD3/I4vj11184SFk8Yw", - "MCtb+KlGZLpZFk4oL9yED4doL6TOuJE+S/8EAAD//w==", + "7FndbtvGEn6VweZcnJNDmrbjgwC6S4Igp03rBHZy1eRixB1Jm5C7zO5SPxAE9CH6hH2SYn9I/dGOlEpW", + "EPTKFjl/nG9mdmZ2znJVVkqStIb15kyTqZQ05H/0kWv6UpOx7hcnk2tRWaEk67HnyG/iu0XCNNUG+wU1", + "7I4+V9KS9KxYVYXI0bFmn4zjnzOTj6hE99+/NA1Yjz3KlqZk4a3JaIplVRBbLBbJhgVvXrOEjQg5aW9t", + "+PcifMWXWmjirGd1TcmKLjuriPWYsVrIIXNCA9vlTmxCWhqSdtY41mikI2js7M1ZpVVF2orgwzEWNXVr", + "jk9U/xPl3osGS7rGskOKjE/XhSRsmiqsRJorTkOSKU2txtTi0DNVqLFkvcC86NDoHgk5UNvovlDSopAG", + "uBgMSJO0EOEEJ8OAqatKaUsc+jNw5uQWDOkxaZYwK6xzBbtdfQ7RRYYlbEzaBEUXZ+dn5+7bVUUSK8F6", + "7Il/lLAK7ch/RxsyleqKxJ9v31yDMIC1VSVakWNRzKBEbUZYEAchrXIm1rk1Z8xr0j4Uf+KR+2UEL2Ex", + "3J8rPjtGCPtMWUmwy/PzB8qURcKugrIuGa1R2UrKezEDrIsOn7+Xn6WaSCCtlY5flpV1YUWF2q5ite7t", + "XxuSXVzeyssGSpcpR4tH8vqhNJ3a8ammAq0rYF8F4CZQ7ofDivijovB39JwUg3gCdNap25GaGBipCVgF", + "nLCAibAjaBg3CqyQgGCEHBYEjVFJJ5gFxYP4meQ38VveORlHr2fJmpRpOplMUp9AtS5IukOJf5tYUeKQ", + "skoO19mdbLSsx/oz60J2+0g9UCInzNLUZlWBYsMxmyofqKT/4+mDJXZIV6nSCFG60kJ2J+51Swv/vjy/", + "+g80gkMCK0+HBaDkIOuicI3wkiaKhz9//wNoSjoXhgzYEVqopSHbEqAmUKWwrqkaaFWCHS3FbOX+tXoR", + "bPp/NH8rDq+6vgQi13rr3FgdfbEORPOy6Yq3Y6HxQCf7dsYEBJpmO3U5kfZjhepGIBY4cFSu1Wt4EzAK", + "SmFcnQwvzUjVBQcsJjgzoUJv93w3kd31fr40Hr3xSzZmix+7EWyhdbl9Gmjf0dR+Fdo9Ss++8D10Vdsf", + "Ij+V8XSo0s80myjNUz8vkiVtsrmzc+FkDalD5NuWEnKU0CdwMyYHHFjS8EpBFGk68Al6X6nXgWQpyo98", + "7Y/eb3PmnOfHQJbECTj4L9ljwP94XKgab4b1R7qm6q6AjySN6zQNjGsJuzDu8F/QdLNCcZqh9f7Y3FoI", + "PURQGywpdYESQjlFyX31SeMyZZbN3dvFfeAMNRkjlATrqtJAaRDG1ASPLp7+72kPEFw8QhuoUNbGglTW", + "IdlXteQfpF84YNO1h+LVWACfHH2fcqwNuRPe1TV38qNPobMPcgvy27gV8pnyTHKH89sobqec8X/2zpnD", + "x1S73jpy27yqZzNnX+Yj5dsvWsenj/nnJoxcQbh7gnYnyy5T8wEniO/9MKnDw7t9Frl2cds3DiQ7eHEs", + "OKmsrK72lHwyp5qKcjEQxNtRJdh2V/F6oWSuya5vElxP5cpTKwz6Mx/+wQO+zZpQKGMVGgPC+sOoEGHp", + "y7dHj/dLy+I08W55Kt+H6uMjYfr4VIhenV/sz/LkyHGzthG4Ix9vfnkZaPZdfR9s9bDnCXA4vSdK54mw", + "o6+vGuIwv2wNcxJj11hLDppsrSVxGAts7jO2cjMKWMLa1R7EMb1tEJqbs316hOReWZfs3tuzjz/wTcvp", + "7iSTB97jPFTW1FLcd//3Xvp2PRxBmyeVUPI7vd3DwpKWaMWY/nuYtfC2FCXpzcDn/QZ6yY4aPn5/9+7H", + "jrpFwsKFdSiYtS5cVbO26mVZuOg+MxMcDkmfCZVhJZyX/goAAP//", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/echo/server.go b/internal/test/servers/strict/echo/server.go index 1f4c633fae..5a4eb5f2d6 100644 --- a/internal/test/servers/strict/echo/server.go +++ b/internal/test/servers/strict/echo/server.go @@ -154,3 +154,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/echo/types.gen.go b/internal/test/servers/strict/echo/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/echo/types.gen.go +++ b/internal/test/servers/strict/echo/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/echo5/server.gen.go b/internal/test/servers/strict/echo5/server.gen.go index a676f7c8f9..ae1423d75d 100644 --- a/internal/test/servers/strict/echo5/server.gen.go +++ b/internal/test/servers/strict/echo5/server.gen.go @@ -9,6 +9,7 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "mime" @@ -53,6 +54,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx *echo.Context) error + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx *echo.Context, name string) error + // (POST /text) TextExample(ctx *echo.Context) error @@ -165,6 +169,22 @@ func (w *ServerInterfaceWrapper) ReusableResponses(ctx *echo.Context) error { return err } +// SameNameParamAndBodyProperty converts echo context to params. +func (w *ServerInterfaceWrapper) SameNameParamAndBodyProperty(ctx *echo.Context) error { + var err error + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", ctx.Param("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter name: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.SameNameParamAndBodyProperty(ctx, name) + return err +} + // TextExample converts echo context to params. func (w *ServerInterfaceWrapper) TextExample(ctx *echo.Context) error { var err error @@ -312,6 +332,7 @@ func RegisterHandlersWithOptions(router EchoRouter, si ServerInterface, options router.POST(options.BaseURL+"/required-text-body", wrapper.RequiredTextBody, options.OperationMiddlewares["RequiredTextBody"]...) router.GET(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters, options.OperationMiddlewares["ReservedGoKeywordParameters"]...) router.POST(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses, options.OperationMiddlewares["ReusableResponses"]...) + router.POST(options.BaseURL+"/same-name-param-and-body-property/:name", wrapper.SameNameParamAndBodyProperty, options.OperationMiddlewares["SameNameParamAndBodyProperty"]...) router.POST(options.BaseURL+"/text", wrapper.TextExample, options.OperationMiddlewares["TextExample"]...) router.POST(options.BaseURL+"/unknown", wrapper.UnknownExample, options.OperationMiddlewares["UnknownExample"]...) router.POST(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType, options.OperationMiddlewares["UnspecifiedContentType"]...) @@ -693,6 +714,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1026,6 +1070,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1073,9 +1120,12 @@ func (sh *strictHandler) JSONExample(ctx *echo.Context) error { err = ctx.Bind(&body) } if err != nil { - return err + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body } - request.Body = &body handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { return sh.ssi.JSONExample(ctx.Request().Context(), request.(JSONExampleRequestObject)) @@ -1173,9 +1223,12 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *echo.Context) erro err = ctx.Bind(&body) } if err != nil { - return err + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.JSONBody = &body } - request.JSONBody = &body } if strings.HasPrefix(ctx.Request().Header.Get("Content-Type"), "application/x-www-form-urlencoded") { if form, err := ctx.FormValues(); err == nil { @@ -1359,9 +1412,12 @@ func (sh *strictHandler) ReusableResponses(ctx *echo.Context) error { err = ctx.Bind(&body) } if err != nil { - return err + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body } - request.Body = &body handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { return sh.ssi.ReusableResponses(ctx.Request().Context(), request.(ReusableResponsesRequestObject)) @@ -1382,6 +1438,50 @@ func (sh *strictHandler) ReusableResponses(ctx *echo.Context) error { return nil } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(ctx *echo.Context, name string) error { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + var err error + if _, ok := ctx.Echo().Binder.(*echo.DefaultBinder); ok { + // Bind only the request body, so that path and query parameters + // are not also bound into the body struct. + err = echo.BindBody(ctx, &body) + } else { + // A custom binder is installed on the Echo instance; defer to it + // entirely, since echo.Binder does not expose body-only binding. + err = ctx.Bind(&body) + } + if err != nil { + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body + } + + handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx.Request().Context(), request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + return validResponse.VisitSameNameParamAndBodyPropertyResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + // TextExample operation middleware func (sh *strictHandler) TextExample(ctx *echo.Context) error { var request TextExampleRequestObject @@ -1515,9 +1615,12 @@ func (sh *strictHandler) HeadersExample(ctx *echo.Context, params HeadersExample err = ctx.Bind(&body) } if err != nil { - return err + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body } - request.Body = &body handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { return sh.ssi.HeadersExample(ctx.Request().Context(), request.(HeadersExampleRequestObject)) @@ -1554,9 +1657,12 @@ func (sh *strictHandler) UnionExample(ctx *echo.Context) error { err = ctx.Bind(&body) } if err != nil { - return err + if !errors.Is(err, io.EOF) { + return err + } + } else { + request.Body = &body } - request.Body = &body handler := func(ctx *echo.Context, request interface{}) (interface{}, error) { return sh.ssi.UnionExample(ctx.Request().Context(), request.(UnionExampleRequestObject)) @@ -1582,27 +1688,30 @@ func (sh *strictHandler) UnionExample(ctx *echo.Context) error { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/echo5/server.go b/internal/test/servers/strict/echo5/server.go index 1f4c633fae..5a4eb5f2d6 100644 --- a/internal/test/servers/strict/echo5/server.go +++ b/internal/test/servers/strict/echo5/server.go @@ -154,3 +154,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/echo5/types.gen.go b/internal/test/servers/strict/echo5/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/echo5/types.gen.go +++ b/internal/test/servers/strict/echo5/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/fiber/server.gen.go b/internal/test/servers/strict/fiber/server.gen.go index aa69ce929a..e66757304e 100644 --- a/internal/test/servers/strict/fiber/server.gen.go +++ b/internal/test/servers/strict/fiber/server.gen.go @@ -53,6 +53,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(c *fiber.Ctx) error + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(c *fiber.Ctx, name string) error + // (POST /text) TextExample(c *fiber.Ctx) error @@ -254,6 +257,35 @@ func (siw *ServerInterfaceWrapper) ReusableResponses(c *fiber.Ctx) error { return handler(c) } +// SameNameParamAndBodyProperty operation middleware +func (siw *ServerInterfaceWrapper) SameNameParamAndBodyProperty(c *fiber.Ctx) error { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", c.Params("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter name: %w", err).Error()) + } + + handler := func(c *fiber.Ctx) error { + return siw.Handler.SameNameParamAndBodyProperty(c, name) + } + + for i := len(siw.HandlerMiddlewares) - 1; i >= 0; i-- { + m := siw.HandlerMiddlewares[i] + next := handler + handler = func(c *fiber.Ctx) error { + return m(c, next) + } + } + + return handler(c) +} + // TextExample operation middleware func (siw *ServerInterfaceWrapper) TextExample(c *fiber.Ctx) error { @@ -448,6 +480,8 @@ func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, option router.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) + router.Post(options.BaseURL+"/same-name-param-and-body-property/:name", wrapper.SameNameParamAndBodyProperty) + router.Post(options.BaseURL+"/text", wrapper.TextExample) router.Post(options.BaseURL+"/unknown", wrapper.UnknownExample) @@ -807,6 +841,24 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(ctx *fiber.Ctx) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(ctx *fiber.Ctx) error { + ctx.Response().Header.Set("Content-Type", "application/json") + ctx.Status(200) + + return ctx.JSON(&response) +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1122,6 +1174,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1450,6 +1505,42 @@ func (sh *strictHandler) ReusableResponses(ctx *fiber.Ctx) error { return nil } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(ctx *fiber.Ctx, name string) error { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := ctx.BodyParser(&body); err != nil { + if !errors.Is(err, io.EOF) { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + } else { + request.Body = &body + } + + handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx.UserContext(), request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(ctx, request) + + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(ctx); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + // TextExample operation middleware func (sh *strictHandler) TextExample(ctx *fiber.Ctx) error { var request TextExampleRequestObject @@ -1643,27 +1734,30 @@ func (sh *strictHandler) UnionExample(ctx *fiber.Ctx) error { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/fiber/server.go b/internal/test/servers/strict/fiber/server.go index 1f4c633fae..5a4eb5f2d6 100644 --- a/internal/test/servers/strict/fiber/server.go +++ b/internal/test/servers/strict/fiber/server.go @@ -154,3 +154,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/fiber/types.gen.go b/internal/test/servers/strict/fiber/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/fiber/types.gen.go +++ b/internal/test/servers/strict/fiber/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/gin/server.gen.go b/internal/test/servers/strict/gin/server.gen.go index b5294a5b54..a36ade4880 100644 --- a/internal/test/servers/strict/gin/server.gen.go +++ b/internal/test/servers/strict/gin/server.gen.go @@ -54,6 +54,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(c *gin.Context) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(c *gin.Context, name string) + // (POST /text) TextExample(c *gin.Context) @@ -211,6 +214,31 @@ func (siw *ServerInterfaceWrapper) ReusableResponses(c *gin.Context) { siw.Handler.ReusableResponses(c) } +// SameNameParamAndBodyProperty operation middleware +func (siw *ServerInterfaceWrapper) SameNameParamAndBodyProperty(c *gin.Context) { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", c.Param("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter name: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.SameNameParamAndBodyProperty(c, name) +} + // TextExample operation middleware func (siw *ServerInterfaceWrapper) TextExample(c *gin.Context) { @@ -374,6 +402,7 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options router.POST(options.BaseURL+"/required-text-body", wrapper.RequiredTextBody) router.GET(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) router.POST(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) + router.POST(options.BaseURL+"/same-name-param-and-body-property/:name", wrapper.SameNameParamAndBodyProperty) router.POST(options.BaseURL+"/text", wrapper.TextExample) router.POST(options.BaseURL+"/unknown", wrapper.UnknownExample) router.POST(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType) @@ -754,6 +783,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1087,6 +1139,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1480,6 +1535,42 @@ func (sh *strictHandler) ReusableResponses(ctx *gin.Context) { } } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(ctx *gin.Context, name string) { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(ctx, err) + return + } + } else { + request.Body = &body + } + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx, request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(ctx, request) + + if err != nil { + sh.options.HandlerErrorFunc(ctx, err) + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(ctx.Writer); err != nil { + sh.options.ResponseErrorHandlerFunc(ctx, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(ctx, fmt.Errorf("unexpected response type: %T", response)) + } +} + // TextExample operation middleware func (sh *strictHandler) TextExample(ctx *gin.Context) { var request TextExampleRequestObject @@ -1678,27 +1769,30 @@ func (sh *strictHandler) UnionExample(ctx *gin.Context) { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/gin/server.go b/internal/test/servers/strict/gin/server.go index 1f4c633fae..5a4eb5f2d6 100644 --- a/internal/test/servers/strict/gin/server.go +++ b/internal/test/servers/strict/gin/server.go @@ -154,3 +154,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/gin/types.gen.go b/internal/test/servers/strict/gin/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/gin/types.gen.go +++ b/internal/test/servers/strict/gin/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/gorilla/server.gen.go b/internal/test/servers/strict/gorilla/server.gen.go index e8d9dc439d..f9ef3f7f3b 100644 --- a/internal/test/servers/strict/gorilla/server.gen.go +++ b/internal/test/servers/strict/gorilla/server.gen.go @@ -54,6 +54,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(w http.ResponseWriter, r *http.Request) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) + // (POST /text) TextExample(w http.ResponseWriter, r *http.Request) @@ -220,6 +223,32 @@ func (siw *ServerInterfaceWrapper) ReusableResponses(w http.ResponseWriter, r *h handler.ServeHTTP(w, r) } +// SameNameParamAndBodyProperty operation middleware +func (siw *ServerInterfaceWrapper) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", mux.Vars(r)["name"], &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + 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.SameNameParamAndBodyProperty(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // TextExample operation middleware func (siw *ServerInterfaceWrapper) TextExample(w http.ResponseWriter, r *http.Request) { @@ -485,6 +514,8 @@ func HandlerWithOptions(si ServerInterface, options GorillaServerOptions) http.H r.HandleFunc(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses).Methods(http.MethodPost) + r.HandleFunc(options.BaseURL+"/same-name-param-and-body-property/{name}", wrapper.SameNameParamAndBodyProperty).Methods(http.MethodPost) + r.HandleFunc(options.BaseURL+"/text", wrapper.TextExample).Methods(http.MethodPost) r.HandleFunc(options.BaseURL+"/unknown", wrapper.UnknownExample).Methods(http.MethodPost) @@ -872,6 +903,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1205,6 +1259,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1570,6 +1627,42 @@ func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Reques } } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + } else { + request.Body = &body + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx, request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(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)) + } +} + // TextExample operation middleware func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) { var request TextExampleRequestObject @@ -1768,27 +1861,30 @@ func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/gorilla/server.go b/internal/test/servers/strict/gorilla/server.go index 3d3890d982..a063639a97 100644 --- a/internal/test/servers/strict/gorilla/server.go +++ b/internal/test/servers/strict/gorilla/server.go @@ -130,3 +130,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/gorilla/types.gen.go b/internal/test/servers/strict/gorilla/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/gorilla/types.gen.go +++ b/internal/test/servers/strict/gorilla/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/iris/server.gen.go b/internal/test/servers/strict/iris/server.gen.go index 54c401bab9..ca61a49060 100644 --- a/internal/test/servers/strict/iris/server.gen.go +++ b/internal/test/servers/strict/iris/server.gen.go @@ -53,6 +53,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx iris.Context) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx iris.Context, name string) + // (POST /text) TextExample(ctx iris.Context) @@ -155,6 +158,26 @@ func (w *ServerInterfaceWrapper) ReusableResponses(ctx iris.Context) { w.Handler.ReusableResponses(ctx) } +// SameNameParamAndBodyProperty converts iris context to params. +func (w *ServerInterfaceWrapper) SameNameParamAndBodyProperty(ctx iris.Context) { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", ctx.Params().Get("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + if err != nil { + ctx.StatusCode(http.StatusBadRequest) + ctx.Writef("Invalid format for parameter name: %s", err) + return + } + + // Invoke the callback with all the unmarshaled arguments + w.Handler.SameNameParamAndBodyProperty(ctx, name) +} + // TextExample converts iris context to params. func (w *ServerInterfaceWrapper) TextExample(ctx iris.Context) { @@ -274,6 +297,7 @@ func RegisterHandlersWithOptions(router *iris.Application, si ServerInterface, o router.Post(options.BaseURL+"/required-text-body", wrapper.RequiredTextBody) router.Get(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters) router.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) + router.Post(options.BaseURL+"/same-name-param-and-body-property/:name", wrapper.SameNameParamAndBodyProperty) router.Post(options.BaseURL+"/text", wrapper.TextExample) router.Post(options.BaseURL+"/unknown", wrapper.UnknownExample) router.Post(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType) @@ -629,6 +653,24 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(ctx iris.Context) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(ctx iris.Context) error { + ctx.ResponseWriter().Header().Set("Content-Type", "application/json") + ctx.StatusCode(200) + + return ctx.JSON(&response) +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -944,6 +986,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1319,6 +1364,45 @@ func (sh *strictHandler) ReusableResponses(ctx iris.Context) { } } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(ctx iris.Context, name string) { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := ctx.ReadJSON(&body); err != nil { + if !errors.Is(err, io.EOF) { + ctx.StopWithError(http.StatusBadRequest, err) + return + } + } else { + request.Body = &body + } + + handler := func(ctx iris.Context, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx, request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(ctx); err != nil { + ctx.StopWithError(http.StatusBadRequest, err) + return + } + } else if response != nil { + ctx.Writef("Unexpected response type: %T", response) + return + } +} + // TextExample operation middleware func (sh *strictHandler) TextExample(ctx iris.Context) { var request TextExampleRequestObject @@ -1535,27 +1619,30 @@ func (sh *strictHandler) UnionExample(ctx iris.Context) { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/iris/server.go b/internal/test/servers/strict/iris/server.go index 12b000d733..d802bcda5e 100644 --- a/internal/test/servers/strict/iris/server.go +++ b/internal/test/servers/strict/iris/server.go @@ -153,3 +153,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/iris/types.gen.go b/internal/test/servers/strict/iris/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/iris/types.gen.go +++ b/internal/test/servers/strict/iris/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/stdhttp/server.gen.go b/internal/test/servers/strict/stdhttp/server.gen.go index f3073a462b..a390586d49 100644 --- a/internal/test/servers/strict/stdhttp/server.gen.go +++ b/internal/test/servers/strict/stdhttp/server.gen.go @@ -55,6 +55,9 @@ type ServerInterface interface { // (POST /reusable-responses) ReusableResponses(w http.ResponseWriter, r *http.Request) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) + // (POST /text) TextExample(w http.ResponseWriter, r *http.Request) @@ -221,6 +224,32 @@ func (siw *ServerInterfaceWrapper) ReusableResponses(w http.ResponseWriter, r *h handler.ServeHTTP(w, r) } +// SameNameParamAndBodyProperty operation middleware +func (siw *ServerInterfaceWrapper) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request) { + + var err error + _ = err + + // ------------- Path parameter "name" ------------- + var name string + + err = runtime.BindStyledParameterWithOptions("simple", "name", r.PathValue("name"), &name, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true, Type: "string", Format: ""}) + 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.SameNameParamAndBodyProperty(w, r, name) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // TextExample operation middleware func (siw *ServerInterfaceWrapper) TextExample(w http.ResponseWriter, r *http.Request) { @@ -484,6 +513,7 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/required-text-body", wrapper.RequiredTextBody) m.HandleFunc(http.MethodGet+" "+options.BaseURL+"/reserved-go-keyword-parameters/{type}", wrapper.ReservedGoKeywordParameters) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/reusable-responses", wrapper.ReusableResponses) + m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/same-name-param-and-body-property/{name}", wrapper.SameNameParamAndBodyProperty) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/text", wrapper.TextExample) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/unknown", wrapper.UnknownExample) m.HandleFunc(http.MethodPost+" "+options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType) @@ -866,6 +896,29 @@ func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse( return nil } +type SameNameParamAndBodyPropertyRequestObject struct { + Name string `json:"name"` + Body *SameNameParamAndBodyPropertyJSONRequestBody +} + +type SameNameParamAndBodyPropertyResponseObject interface { + VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error +} + +type SameNameParamAndBodyProperty200JSONResponse SameName + +func (response SameNameParamAndBodyProperty200JSONResponse) VisitSameNameParamAndBodyPropertyResponse(w http.ResponseWriter) error { + + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(response); err != nil { + return err + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, err := buf.WriteTo(w) + return err +} + type TextExampleRequestObject struct { Body *TextExampleTextRequestBody } @@ -1199,6 +1252,9 @@ type StrictServerInterface interface { // (POST /reusable-responses) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) + // (POST /same-name-param-and-body-property/{name}) + SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) + // (POST /text) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) @@ -1564,6 +1620,42 @@ func (sh *strictHandler) ReusableResponses(w http.ResponseWriter, r *http.Reques } } +// SameNameParamAndBodyProperty operation middleware +func (sh *strictHandler) SameNameParamAndBodyProperty(w http.ResponseWriter, r *http.Request, name string) { + var request SameNameParamAndBodyPropertyRequestObject + + request.Name = name + + var body SameNameParamAndBodyPropertyJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + if !errors.Is(err, io.EOF) { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + } else { + request.Body = &body + } + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.SameNameParamAndBodyProperty(ctx, request.(SameNameParamAndBodyPropertyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SameNameParamAndBodyProperty") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(SameNameParamAndBodyPropertyResponseObject); ok { + if err := validResponse.VisitSameNameParamAndBodyPropertyResponse(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)) + } +} + // TextExample operation middleware func (sh *strictHandler) TextExample(w http.ResponseWriter, r *http.Request) { var request TextExampleRequestObject @@ -1762,27 +1854,30 @@ func (sh *strictHandler) UnionExample(w http.ResponseWriter, r *http.Request) { // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/test/servers/strict/stdhttp/server.go b/internal/test/servers/strict/stdhttp/server.go index 3d24a5d272..efb1f30828 100644 --- a/internal/test/servers/strict/stdhttp/server.go +++ b/internal/test/servers/strict/stdhttp/server.go @@ -156,3 +156,10 @@ func (s StrictServer) UnionExample(ctx context.Context, request UnionExampleRequ }, }, nil } + +func (s StrictServer) SameNameParamAndBodyProperty(ctx context.Context, request SameNameParamAndBodyPropertyRequestObject) (SameNameParamAndBodyPropertyResponseObject, error) { + if request.Body == nil { + return SameNameParamAndBodyProperty200JSONResponse{}, nil + } + return SameNameParamAndBodyProperty200JSONResponse(*request.Body), nil +} diff --git a/internal/test/servers/strict/stdhttp/types.gen.go b/internal/test/servers/strict/stdhttp/types.gen.go index 08ae8548e1..5440e991fd 100644 --- a/internal/test/servers/strict/stdhttp/types.gen.go +++ b/internal/test/servers/strict/stdhttp/types.gen.go @@ -14,6 +14,11 @@ type Example struct { Value *string `json:"value,omitempty"` } +// SameName defines model for sameName. +type SameName struct { + Name *string `json:"name,omitempty" param:"name"` +} + // Reusableresponse defines model for reusableresponse. type Reusableresponse = Example @@ -70,6 +75,9 @@ type RequiredTextBodyTextRequestBody = RequiredTextBodyTextBody // ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType. type ReusableResponsesJSONRequestBody = Example +// SameNameParamAndBodyPropertyJSONRequestBody defines body for SameNameParamAndBodyProperty for application/json ContentType. +type SameNameParamAndBodyPropertyJSONRequestBody = SameName + // TextExampleTextRequestBody defines body for TextExample for text/plain ContentType. type TextExampleTextRequestBody = TextExampleTextBody diff --git a/internal/test/servers/strict/strict-schema.yaml b/internal/test/servers/strict/strict-schema.yaml index fd5f07feda..4dcde75e5d 100644 --- a/internal/test/servers/strict/strict-schema.yaml +++ b/internal/test/servers/strict/strict-schema.yaml @@ -332,6 +332,30 @@ paths: text/plain: schema: type: string + /same-name-param-and-body-property/{name}: + post: + operationId: SameNameParamAndBodyProperty + description: | + Regression test for issue #1757: a path parameter must not be bound + into a request body property just because they share a name. + parameters: + - name: name + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/sameName" + responses: + 200: + description: Echoes the request body back + content: + application/json: + schema: + $ref: "#/components/schemas/sameName" /with-union: post: operationId: UnionExample @@ -391,3 +415,12 @@ components: properties: value: type: string + sameName: + type: object + properties: + name: + type: string + # The param tag is what makes Echo's full Bind copy a same-named + # path parameter into this property; binders skip untagged fields. + x-oapi-codegen-extra-tags: + param: name diff --git a/internal/test/servers/strict/strict_test.go b/internal/test/servers/strict/strict_test.go index c4395b0fe8..e7c76d1f75 100644 --- a/internal/test/servers/strict/strict_test.go +++ b/internal/test/servers/strict/strict_test.go @@ -82,6 +82,16 @@ func testImpl(t *testing.T, handler http.Handler) { assert.NoError(t, err) assert.Equal(t, requestBody, responseBody) }) + t.Run("SameNameParamAndBodyProperty", func(t *testing.T) { + // Regression test for issue #1757: the "name" path parameter must not + // be bound into the request body's same-named "name" property. + rr := testutil.NewRequest().Post("/same-name-param-and-body-property/path-value").WithJsonBody(clientAPI.SameName{}).GoWithHTTPHandler(t, handler).Recorder + assert.Equal(t, http.StatusOK, rr.Code) + var responseBody clientAPI.SameName + err := json.NewDecoder(rr.Body).Decode(&responseBody) + assert.NoError(t, err) + assert.Nil(t, responseBody.Name) + }) t.Run("URLEncodedExample", func(t *testing.T) { value := "456" requestBody := clientAPI.Example{Value: &value} diff --git a/pkg/codegen/templates/strict/strict-echo5.tmpl b/pkg/codegen/templates/strict/strict-echo5.tmpl index e1c5ca1c3b..551759c9e8 100644 --- a/pkg/codegen/templates/strict/strict-echo5.tmpl +++ b/pkg/codegen/templates/strict/strict-echo5.tmpl @@ -45,9 +45,16 @@ type strictHandler struct { err = ctx.Bind(&body) } if err != nil { + {{if not .Required -}} + if !errors.Is(err, io.EOF) { + return err + } + {{else -}} return err - } + {{end -}} + } {{if not .Required -}} else { {{end}} request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body + {{if not .Required -}} } {{end}} {{else if eq .NameTag "Formdata" -}} if form, err := ctx.FormValues(); err == nil { var body {{$opid}}{{.NameTag}}RequestBody