oapi-codegen icon indicating copy to clipboard operation
oapi-codegen copied to clipboard

If paths contain external references, code generation might fail

Open ShouheiNishi opened this issue 2 years ago • 4 comments

If paths contain an external reference and the schema of the operation parameter is an array, code generation fails.

ShouheiNishi avatar Jan 31 '24 06:01 ShouheiNishi

Test code is https://github.com/ShouheiNishi/oapi-codegen/tree/2037fbb269b13ce052aa8c7a803a68c2178d44de/internal/test/issues/issue-1452.

$ git status
HEAD detached at 2037fbb
nothing to commit, working tree clean
$ (cd internal/test ; go generate ./issues/issue-1452/...)
error generating code: error formatting Go code // Package pkg1 provides primitives to interact with the openapi HTTP API.
//
// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.0.0-00010101000000-000000000000 DO NOT EDIT.
package pkg1

import (
        "bytes"
        "compress/gzip"
        "context"
        "encoding/base64"
        "encoding/json"
        "encoding/xml"
        "errors"
        "fmt"
        "gopkg.in/yaml.v2"
        "io"
        "os"
        "mime"
        "mime/multipart"
        "net/http"
        "net/url"
        "path"
        "strings"
        "time"

        "github.com/oapi-codegen/runtime"
        strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo"
        strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin"
        strictiris "github.com/oapi-codegen/runtime/strictmiddleware/iris"
        strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp"
        openapi_types "github.com/oapi-codegen/runtime/types"
        "github.com/getkin/kin-openapi/openapi3"
        "github.com/go-chi/chi/v5"
        "github.com/labstack/echo/v4"
        "github.com/gin-gonic/gin"
        "github.com/gofiber/fiber/v2"
        "github.com/kataras/iris/v12"
        "github.com/kataras/iris/v12/core/router"
        "github.com/gorilla/mux"
        externalRef0 "github.com/deepmap/oapi-codegen/v2/internal/test/issues/issue-1452/pkg2"
)







// TestParams defines parameters for Test.
type TestParams  struct {
    Param *externalRef0.[]string`form:"param,omitempty" json:"param,omitempty"`
}






// ServerInterface represents all server handlers.
type ServerInterface interface {

// (GET /test)
Test(w http.ResponseWriter, r *http.Request, params TestParams)

}

// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint.

type Unimplemented struct {}

 // (GET /test)
 func (_ Unimplemented) Test(w http.ResponseWriter, r *http.Request, params TestParams) {
        w.WriteHeader(http.StatusNotImplemented)
 }

// ServerInterfaceWrapper converts contexts to parameters.
type ServerInterfaceWrapper struct {
    Handler ServerInterface
    HandlerMiddlewares []MiddlewareFunc
    ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error)
}

type MiddlewareFunc func(http.Handler) http.Handler



// Test operation middleware
func (siw *ServerInterfaceWrapper) Test(w http.ResponseWriter, r *http.Request) {
  ctx := r.Context()

  var err error







    // Parameter object where we will unmarshal all parameters from the context
    var params TestParams

    // ------------- Optional query parameter "param" -------------



      err = runtime.BindQueryParameter("form", true, false, "param", r.URL.Query(), &params.Param)
      if err != nil {
        siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "param", Err: err})
        return
      }








  handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    siw.Handler.Test(w, r, params)
  }))


  for _, middleware := range siw.HandlerMiddlewares {
    handler = middleware(handler)
  }


  handler.ServeHTTP(w, r.WithContext(ctx))
}


type UnescapedCookieParamError struct {
    ParamName string
        Err error
}

func (e *UnescapedCookieParamError) Error() string {
    return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName)
}

func (e *UnescapedCookieParamError) Unwrap() error {
    return e.Err
}

type UnmarshalingParamError struct {
    ParamName string
    Err error
}

func (e *UnmarshalingParamError) Error() string {
    return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error())
}

func (e *UnmarshalingParamError) Unwrap() error {
    return e.Err
}

type RequiredParamError struct {
    ParamName string
}

func (e *RequiredParamError) Error() string {
    return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName)
}

type RequiredHeaderError struct {
    ParamName string
    Err error
}

func (e *RequiredHeaderError) Error() string {
    return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName)
}

func (e *RequiredHeaderError) Unwrap() error {
    return e.Err
}

type InvalidParamFormatError struct {
    ParamName string
          Err error
}

func (e *InvalidParamFormatError) Error() string {
    return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error())
}

func (e *InvalidParamFormatError) Unwrap() error {
    return e.Err
}

type TooManyValuesForParamError struct {
    ParamName string
    Count int
}

func (e *TooManyValuesForParamError) Error() string {
    return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count)
}

// Handler creates http.Handler with routing matching OpenAPI spec.
func Handler(si ServerInterface) http.Handler {
  return HandlerWithOptions(si, ChiServerOptions{})
}

type ChiServerOptions struct {
    BaseURL string
    BaseRouter chi.Router
    Middlewares []MiddlewareFunc
    ErrorHandlerFunc   func(w http.ResponseWriter, r *http.Request, err error)
}

// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
    return HandlerWithOptions(si, ChiServerOptions {
        BaseRouter: r,
    })
}

func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler {
    return HandlerWithOptions(si, ChiServerOptions {
        BaseURL: baseURL,
        BaseRouter: r,
    })
}

// HandlerWithOptions creates http.Handler with additional options
func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler {
r := options.BaseRouter

if r == nil {
r = chi.NewRouter()
}
if options.ErrorHandlerFunc == nil {
    options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) {
        http.Error(w, err.Error(), http.StatusBadRequest)
    }
}
wrapper := ServerInterfaceWrapper{
Handler: si,
HandlerMiddlewares: options.Middlewares,
ErrorHandlerFunc: options.ErrorHandlerFunc,
}

r.Group(func(r chi.Router) {
r.Get(options.BaseURL+"/test", wrapper.Test)
})

return r
}
: pkg1.go:51:25: expected 'IDENT', found '[' (and 1 more errors)
exit status 1
issues/issue-1452/pkg1/doc.go:3: running "go": exit status 1
$

ShouheiNishi avatar Jan 31 '24 06:01 ShouheiNishi

I am experiencing exactly the same issue., I get code generated like:

KnownRenderedVersion *externalRef0.string `form:"knownRenderedVersion,omitempty" json:"knownRenderedVersion,omitempty"

or

	// DeleteEnrollmentRequest request
	DeleteEnrollmentRequest(ctx context.Context, name externalRef0.string, reqEditors ...RequestEditorFn) (*http.Response, error)

	// ReadEnrollmentRequest request
	ReadEnrollmentRequest(ctx context.Context, name externalRef0.string, reqEditors ...RequestEditorFn) (*http.Response, error)

which is not valid

mangelajo avatar Jun 18 '24 12:06 mangelajo

If paths contain an external reference and the schema of the operation parameter is an array, code generation fails.

Also happens for simple strings, no need for it to be an array.

Tested with v2.3.0

mangelajo avatar Jun 18 '24 12:06 mangelajo

An example can be found here https://github.com/flightctl/flightctl/pull/295/commits/437743d4fcb98136af82143fbe03ae683662a920

mangelajo avatar Jun 18 '24 12:06 mangelajo