diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6f3a829..1ef1695 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -8,10 +8,15 @@ on: jobs: test: runs-on: ubuntu-latest + env: + GOEXPERIMENT: "rangefunc" + steps: - uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: '^1.22' - - run: make test.race \ No newline at end of file + - run: make test.race + + diff --git a/devpkg/clientgen/gen.go b/devpkg/clientgen/gen.go index 47e30e7..4bbe5cf 100644 --- a/devpkg/clientgen/gen.go +++ b/devpkg/clientgen/gen.go @@ -1,4 +1,4 @@ -package openapi +package clientgen import ( "context" @@ -428,11 +428,11 @@ const ( `, "enums": gengo.MapSnippet(enumType.Enum, func(enum any) gengo.Snippet { return gengo.Snippet{gengo.T: ` -@NamePrefix'__@Name @Type = @value +@NamePrefix'__@OrgName @Type = @value `, "Type": gengo.ID(gengo.UpperCamelCase(name)), "NamePrefix": gengo.ID(gengo.UpperSnakeCase(name)), - "Name": gengo.ID(gengo.UpperCamelCase(enum.(string))), + "OrgName": gengo.ID(gengo.UpperCamelCase(enum.(string))), "value": enum, } }), diff --git a/devpkg/operatorgen/gen.go b/devpkg/operatorgen/gen.go index 0e96c46..95b2f31 100644 --- a/devpkg/operatorgen/gen.go +++ b/devpkg/operatorgen/gen.go @@ -1,4 +1,4 @@ -package openapi +package operatorgen import ( "fmt" @@ -15,7 +15,7 @@ import ( "github.com/octohelm/courier/pkg/courierhttp" "github.com/octohelm/gengo/pkg/gengo" gengotypes "github.com/octohelm/gengo/pkg/types" - typesutil "github.com/octohelm/x/types" + typex "github.com/octohelm/x/types" ) func init() { @@ -36,7 +36,7 @@ func (g *operatorGen) GenerateType(c gengo.Context, named *types.Named) error { return gengo.ErrSkip } - if !isCourierOperator(c, typesutil.FromTType(types.NewPointer(named)), g.resolvePkg) { + if !isCourierOperator(c, typex.FromTType(types.NewPointer(named)), g.resolvePkg) { return gengo.ErrSkip } @@ -46,12 +46,12 @@ func (g *operatorGen) GenerateType(c gengo.Context, named *types.Named) error { } func (g *operatorGen) generateReturns(c gengo.Context, named *types.Named) { - method, ok := typesutil.FromTType(types.NewPointer(named)).MethodByName("Output") + method, ok := typex.FromTType(types.NewPointer(named)).MethodByName("Output") if ok { - results, n := c.Package(named.Obj().Pkg().Path()).ResultsOf(method.(*typesutil.TMethod).Func) + results, n := c.Package(named.Obj().Pkg().Path()).ResultsOf(method.(*typex.TMethod).Func) if n == 2 { g.generateSuccessReturn(c, named, results[0]) - g.generateErrorsReturn(c, named, method.(*typesutil.TMethod).Func) + g.generateErrorsReturn(c, named, method.(*typex.TMethod).Func) } } } @@ -222,9 +222,9 @@ func (g *operatorGen) resolvePkg(c gengo.Context, importPath string) *types.Pack } func (g *operatorGen) firstValueOfFunc(c gengo.Context, named *types.Named, name string) (interface{}, bool) { - method, ok := typesutil.FromTType(types.NewPointer(named)).MethodByName(name) + method, ok := typex.FromTType(types.NewPointer(named)).MethodByName(name) if ok { - fn := method.(*typesutil.TMethod).Func + fn := method.(*typex.TMethod).Func results, n := c.Package(fn.Pkg().Path()).ResultsOf(fn) if n == 1 { for _, r := range results[0] { @@ -240,11 +240,11 @@ func (g *operatorGen) firstValueOfFunc(c gengo.Context, named *types.Named, name var typOperator = reflect.TypeOf((*courier.Operator)(nil)).Elem() -func isCourierOperator(c gengo.Context, tpe typesutil.Type, lookup func(c gengo.Context, importPath string) *types.Package) bool { +func isCourierOperator(c gengo.Context, tpe typex.Type, lookup func(c gengo.Context, importPath string) *types.Package) bool { switch tpe.(type) { - case *typesutil.RType: - return tpe.Implements(typesutil.FromRType(typOperator)) - case *typesutil.TType: + case *typex.RType: + return tpe.Implements(typex.FromRType(typOperator)) + case *typex.TType: pkg := lookup(c, typOperator.PkgPath()) if pkg == nil { return false @@ -253,7 +253,7 @@ func isCourierOperator(c gengo.Context, tpe typesutil.Type, lookup func(c gengo. if t == nil { return false } - return types.Implements(tpe.(*typesutil.TType).Type, t.Type().Underlying().(*types.Interface)) + return types.Implements(tpe.(*typex.TType).Type, t.Type().Underlying().(*types.Interface)) } return false } diff --git a/devpkg/operatorgen/statuserr_scanner.go b/devpkg/operatorgen/statuserr_scanner.go index 3092015..bacfcdd 100644 --- a/devpkg/operatorgen/statuserr_scanner.go +++ b/devpkg/operatorgen/statuserr_scanner.go @@ -1,9 +1,14 @@ -package openapi +package operatorgen import ( "fmt" + typex "github.com/octohelm/x/types" "go/ast" + "go/constant" + "go/token" "go/types" + "net/http" + "path/filepath" "reflect" "sort" "strconv" @@ -74,6 +79,10 @@ func (s *statusErrScanner) StatusErrorsInFunc(ctx gengo.Context, typeFunc *types tpe = p.Elem() } if named, ok := tpe.(*types.Named); ok { + if isErrWithStatusCodeInterface(named) { + return s.scanErrWithStatusCodeInterface(ctx, named) + } + if isTypeStatusErr(named) { ast.Inspect(r.Expr, func(node ast.Node) bool { switch x := node.(type) { @@ -128,7 +137,6 @@ func (s *statusErrScanner) appendStateErrs(typeFunc *types.Func, statusErrs ...* func (s *statusErrScanner) scanStatusErrIsExist(typeFunc *types.Func, pkg gengotypes.Package, obj types.Object, callIdent *ast.Ident, x *ast.CallExpr) bool { if callIdent.Name == "Wrap" && obj.Pkg().Path() == statusErr.PkgPath() { - code := 0 key := "" msg := "" @@ -171,6 +179,126 @@ func (s *statusErrScanner) scanStatusErrIsExist(typeFunc *types.Func, pkg gengot return false } +var ( + rtypeErrorWithStatusCode = typex.FromRType(reflect.TypeOf((*statuserror.ErrorWithStatusCode)(nil)).Elem()) +) + +func isErrWithStatusCodeInterface(named *types.Named) bool { + if named != nil { + return typex.FromTType(types.NewPointer(named)).Implements(rtypeErrorWithStatusCode) + } + return false +} + +func (s *statusErrScanner) resolveStateCode(ctx gengo.Context, named *types.Named) (int, bool) { + method, ok := typex.FromTType(types.NewPointer(named)).MethodByName("StatusCode") + if ok { + m := method.(*typex.TMethod) + if m.Func.Pkg() == nil { + return 0, false + } + + results, n := ctx.Package(m.Func.Pkg().Path()).ResultsOf(m.Func) + if n == 1 { + for _, r := range results[0] { + if r.Value != nil && r.Value.Kind() == constant.Int { + v, err := strconv.ParseInt(r.Value.String(), 10, 64) + if err == nil { + return int(v), true + } + } + } + } + } + + return 0, false +} + +func (s *statusErrScanner) scanErrWithStatusCodeInterface(ctx gengo.Context, named *types.Named) (list []*statuserror.StatusErr) { + if named.Obj() == nil { + return nil + } + + serr := &statuserror.StatusErr{ + Key: filepath.Base(named.Obj().Pkg().Path()) + "." + named.Obj().Name(), + Code: http.StatusInternalServerError, + } + + code, ok := s.resolveStateCode(ctx, named) + if ok { + serr.Code = code + } + + method, ok := typex.FromTType(types.NewPointer(named)).MethodByName("Error") + if ok { + m := method.(*typex.TMethod) + if m.Func.Pkg() == nil { + return + } + + results, n := ctx.Package(m.Func.Pkg().Path()).ResultsOf(m.Func) + if n == 1 { + for _, r := range results[0] { + switch x := r.Expr.(type) { + case *ast.BasicLit: + str, err := strconv.Unquote(x.Value) + if err == nil { + e := &(*serr) + e.Msg = str + list = append(list, e) + } + case *ast.CallExpr: + if selectExpr, ok := x.Fun.(*ast.SelectorExpr); ok { + if selectExpr.Sel.Name == "Sprintf" { + e := &(*serr) + e.Msg = fmtSprintfArgsAsTemplate(x.Args) + list = append(list, e) + } + } + } + } + } + } + + return +} + +func fmtSprintfArgsAsTemplate(args []ast.Expr) string { + if len(args) == 0 { + return "" + } + + f := "" + fArgs := make([]any, 0, len(args)) + + toString := func(a *ast.BasicLit) string { + switch a.Kind { + case token.STRING: + v, _ := strconv.Unquote(a.Value) + return v + default: + return a.Value + } + } + + for i, arg := range args { + switch a := arg.(type) { + case *ast.BasicLit: + if i == 0 { + f = toString(a) + } else { + fArgs = append(fArgs, toString(a)) + } + case *ast.SelectorExpr: + fArgs = append(fArgs, fmt.Sprintf("{%s}", a.Sel.Name)) + case *ast.Ident: + fArgs = append(fArgs, fmt.Sprintf("{%s}", a.Name)) + } + } + + return fmt.Sprintf(normalizeFormat(f), fArgs...) +} + func pickStatusErrorsFromDoc(lines []string) []*statuserror.StatusErr { statusErrorList := make([]*statuserror.StatusErr, 0) diff --git a/devpkg/operatorgen/util.go b/devpkg/operatorgen/util.go new file mode 100644 index 0000000..97fa346 --- /dev/null +++ b/devpkg/operatorgen/util.go @@ -0,0 +1,14 @@ +package operatorgen + +import ( + "regexp" +) + +// https://pkg.go.dev/fmt#hdr-Printing +var re = regexp.MustCompile(`%(\[([0-9]+)])?(([.0-9]+)|([#-+ 0]))?[vTtbcdoOqxXUeEfFgGps]`) + +func normalizeFormat(s string) string { + return re.ReplaceAllStringFunc(s, func(seg string) string { + return "%v" + }) +} diff --git a/devpkg/operatorgen/util_test.go b/devpkg/operatorgen/util_test.go new file mode 100644 index 0000000..27608d0 --- /dev/null +++ b/devpkg/operatorgen/util_test.go @@ -0,0 +1,30 @@ +package operatorgen + +import ( + "fmt" + "testing" +) + +func Test_normalizeFormat(t *testing.T) { + cases := []struct { + actual string + expect string + }{ + { + "%s %q %v %.1f %q %%", + "%v %v %v %v %v %%", + }, + { + "%[2]s %[1]q", + "%v %v", + }, + } + + for _, tc := range cases { + t.Run(fmt.Sprintf("%q", tc.actual), func(t *testing.T) { + if expect := normalizeFormat(tc.actual); expect != tc.expect { + t.Fatalf("expect: %q, actual: %q", tc.expect, tc.actual) + } + }) + } +} diff --git a/example/apis/blob/zz_generated.operator.go b/example/apis/blob/zz_generated.operator.go index d0f6714..d21ad82 100644 --- a/example/apis/blob/zz_generated.operator.go +++ b/example/apis/blob/zz_generated.operator.go @@ -1,5 +1,5 @@ /* -Package blob GENERATED BY gengo:operator +Package blob GENERATED BY gengo:operator DON'T EDIT THIS FILE */ package blob diff --git a/example/apis/blob/zz_generated.runtimedoc.go b/example/apis/blob/zz_generated.runtimedoc.go index d9c4f43..1fbc65b 100644 --- a/example/apis/blob/zz_generated.runtimedoc.go +++ b/example/apis/blob/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package blob GENERATED BY gengo:runtimedoc +Package blob GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package blob diff --git a/example/apis/org/errors.go b/example/apis/org/errors.go new file mode 100644 index 0000000..23d2d8b --- /dev/null +++ b/example/apis/org/errors.go @@ -0,0 +1,17 @@ +package org + +import ( + "fmt" + + "github.com/octohelm/courier/example/pkg/statuserr" +) + +type ErrNotFound struct { + statuserr.NotFound + + OrgName string +} + +func (e ErrNotFound) Error() string { + return fmt.Sprintf("%s: 组织不存在", e.OrgName) +} diff --git a/example/apis/org/operator/zz_generated.runtimedoc.go b/example/apis/org/operator/zz_generated.runtimedoc.go index 6675efb..dba2322 100644 --- a/example/apis/org/operator/zz_generated.runtimedoc.go +++ b/example/apis/org/operator/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package operator GENERATED BY gengo:runtimedoc +Package operator GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package operator diff --git a/example/apis/org/org__get.go b/example/apis/org/org__get.go index b9cbacb..e1c9024 100644 --- a/example/apis/org/org__get.go +++ b/example/apis/org/org__get.go @@ -2,16 +2,11 @@ package org import ( "context" - "net/http" "time" "github.com/octohelm/courier/example/apis/org/operator" "github.com/octohelm/courier/pkg/courier" - - "github.com/pkg/errors" - "github.com/octohelm/courier/pkg/courierhttp" - "github.com/octohelm/courier/pkg/statuserror" ) func (GetOrg) MiddleOperators() courier.MiddleOperators { @@ -23,12 +18,13 @@ func (GetOrg) MiddleOperators() courier.MiddleOperators { // 查询组织信息 type GetOrg struct { courierhttp.MethodGet `path:"/:orgName"` - OrgName string `name:"orgName" in:"path" ` + + OrgName string `name:"orgName" in:"path" ` } func (c *GetOrg) Output(ctx context.Context) (any, error) { if c.OrgName == "NotFound" { - return nil, statuserror.Wrap(errors.New("NotFound"), http.StatusNotFound, "NotFound") + return nil, &ErrNotFound{OrgName: c.OrgName} } return &Detail{ diff --git a/example/apis/org/zz_generated.enum.go b/example/apis/org/zz_generated.enum.go index ffdfd0f..53027d2 100644 --- a/example/apis/org/zz_generated.enum.go +++ b/example/apis/org/zz_generated.enum.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:enum +Package org GENERATED BY gengo:enum DON'T EDIT THIS FILE */ package org diff --git a/example/apis/org/zz_generated.operator.go b/example/apis/org/zz_generated.operator.go index aad142a..bb77ca7 100644 --- a/example/apis/org/zz_generated.operator.go +++ b/example/apis/org/zz_generated.operator.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:operator +Package org GENERATED BY gengo:operator DON'T EDIT THIS FILE */ package org @@ -41,8 +41,8 @@ func (*GetOrg) ResponseErrors() []error { return []error{ &(github_com_octohelm_courier_pkg_statuserror.StatusErr{ Code: 404, - Key: "NotFound", - Msg: "NotFound", + Key: "org.ErrNotFound", + Msg: "{OrgName}: 组织不存在", }), } } diff --git a/example/apis/org/zz_generated.runtimedoc.go b/example/apis/org/zz_generated.runtimedoc.go index d4c5f66..9b21f1c 100644 --- a/example/apis/org/zz_generated.runtimedoc.go +++ b/example/apis/org/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package org GENERATED BY gengo:runtimedoc +Package org GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package org @@ -95,6 +95,19 @@ func (v Detail) RuntimeDoc(names ...string) ([]string, bool) { return []string{}, true } +func (v ErrNotFound) RuntimeDoc(names ...string) ([]string, bool) { + if len(names) > 0 { + switch names[0] { + case "OrgName": + return []string{}, true + + } + + return nil, false + } + return []string{}, true +} + func (v GetOrg) RuntimeDoc(names ...string) ([]string, bool) { if len(names) > 0 { switch names[0] { diff --git a/example/apis/store/zz_generated.operator.go b/example/apis/store/zz_generated.operator.go index 10e94d3..d90cc0f 100644 --- a/example/apis/store/zz_generated.operator.go +++ b/example/apis/store/zz_generated.operator.go @@ -1,5 +1,5 @@ /* -Package store GENERATED BY gengo:operator +Package store GENERATED BY gengo:operator DON'T EDIT THIS FILE */ package store diff --git a/example/apis/store/zz_generated.runtimedoc.go b/example/apis/store/zz_generated.runtimedoc.go index 41d6f35..60c21a1 100644 --- a/example/apis/store/zz_generated.runtimedoc.go +++ b/example/apis/store/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package store GENERATED BY gengo:runtimedoc +Package store GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package store diff --git a/example/client/example/zz_generated.runtimedoc.go b/example/client/example/zz_generated.runtimedoc.go index 142d4d5..2808920 100644 --- a/example/client/example/zz_generated.runtimedoc.go +++ b/example/client/example/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package example GENERATED BY gengo:runtimedoc +Package example GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package example diff --git a/example/pkg/statuserr/statuserr.go b/example/pkg/statuserr/statuserr.go new file mode 100644 index 0000000..1323ab3 --- /dev/null +++ b/example/pkg/statuserr/statuserr.go @@ -0,0 +1,10 @@ +package statuserr + +import "net/http" + +type NotFound struct { +} + +func (NotFound) StatusCode() int { + return http.StatusNotFound +} diff --git a/example/pkg/statuserr/zz_generated.runtimedoc.go b/example/pkg/statuserr/zz_generated.runtimedoc.go new file mode 100644 index 0000000..cdb61ab --- /dev/null +++ b/example/pkg/statuserr/zz_generated.runtimedoc.go @@ -0,0 +1,15 @@ +/* +Package statuserr GENERATED BY gengo:runtimedoc +DON'T EDIT THIS FILE +*/ +package statuserr + +// nolint:deadcode,unused +func runtimeDoc(v any, names ...string) ([]string, bool) { + if c, ok := v.(interface { + RuntimeDoc(names ...string) ([]string, bool) + }); ok { + return c.RuntimeDoc(names...) + } + return nil, false +} diff --git a/internal/pathpattern/zz_generated.runtimedoc.go b/internal/pathpattern/zz_generated.runtimedoc.go index 597181d..e086c36 100644 --- a/internal/pathpattern/zz_generated.runtimedoc.go +++ b/internal/pathpattern/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package pathpattern GENERATED BY gengo:runtimedoc +Package pathpattern GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package pathpattern diff --git a/pkg/courierhttp/client/zz_generated.runtimedoc.go b/pkg/courierhttp/client/zz_generated.runtimedoc.go index 38a4f1d..f90c826 100644 --- a/pkg/courierhttp/client/zz_generated.runtimedoc.go +++ b/pkg/courierhttp/client/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package client GENERATED BY gengo:runtimedoc +Package client GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package client diff --git a/pkg/courierhttp/errors.go b/pkg/courierhttp/errors.go new file mode 100644 index 0000000..b52aa1f --- /dev/null +++ b/pkg/courierhttp/errors.go @@ -0,0 +1,16 @@ +package courierhttp + +import "fmt" + +type ErrContextCanceled struct { + Reason string +} + +func (ErrContextCanceled) StatusCode() int { + // https://httpstatuses.com/499 + return 499 +} + +func (c *ErrContextCanceled) Error() string { + return fmt.Sprintf("context canceled: %s", c.Reason) +} diff --git a/pkg/courierhttp/handler/httprouter/openapi_view.go b/pkg/courierhttp/handler/httprouter/openapi_view.go index ebf362a..03e7997 100644 --- a/pkg/courierhttp/handler/httprouter/openapi_view.go +++ b/pkg/courierhttp/handler/httprouter/openapi_view.go @@ -2,7 +2,6 @@ package httprouter import ( "context" - "fmt" "net/http" "github.com/octohelm/courier/pkg/courierhttp" @@ -22,8 +21,6 @@ type OpenAPIView struct { } func (o *OpenAPIView) Output(ctx context.Context) (any, error) { - fmt.Println("OpenAPIView") - if openapiView == nil { return nil, statuserror.Wrap( errors.New("openapi view not found"), diff --git a/pkg/courierhttp/handler/httprouter/zz_generated.runtimedoc.go b/pkg/courierhttp/handler/httprouter/zz_generated.runtimedoc.go index 7edaff6..ca3cf3a 100644 --- a/pkg/courierhttp/handler/httprouter/zz_generated.runtimedoc.go +++ b/pkg/courierhttp/handler/httprouter/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package httprouter GENERATED BY gengo:runtimedoc +Package httprouter GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package httprouter diff --git a/pkg/courierhttp/handler/zz_generated.runtimedoc.go b/pkg/courierhttp/handler/zz_generated.runtimedoc.go index a638116..5100c96 100644 --- a/pkg/courierhttp/handler/zz_generated.runtimedoc.go +++ b/pkg/courierhttp/handler/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package handler GENERATED BY gengo:runtimedoc +Package handler GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package handler diff --git a/pkg/courierhttp/openapi/openapi.go b/pkg/courierhttp/openapi/openapi.go index 935cf45..9d47bed 100644 --- a/pkg/courierhttp/openapi/openapi.go +++ b/pkg/courierhttp/openapi/openapi.go @@ -3,6 +3,7 @@ package openapi import ( "context" "fmt" + "github.com/octohelm/courier/pkg/statuserror" "net/http" "reflect" "regexp" @@ -18,7 +19,6 @@ import ( "github.com/octohelm/courier/pkg/courierhttp/transport" "github.com/octohelm/courier/pkg/openapi" "github.com/octohelm/courier/pkg/openapi/jsonschema/extractors" - "github.com/octohelm/courier/pkg/statuserror" transformer "github.com/octohelm/courier/pkg/transformer/core" "github.com/octohelm/gengo/pkg/gengo" typesx "github.com/octohelm/x/types" @@ -169,6 +169,8 @@ func (b *scanner) scan(r courier.Route) error { b.scanResponse(ctx, op, o) } + + b.scanResponseError(ctx, op, o) } b.o.AddOperation(rh.Method(), b.patchPath(rh.Path(), op), op) @@ -265,19 +267,33 @@ func (b *scanner) scanResponse(ctx context.Context, op *openapi.OperationObject, } op.AddResponse(statusCode, resp) +} +func (b *scanner) scanResponseError(ctx context.Context, op *openapi.OperationObject, o *courier.OperatorFactory) { if can, ok := o.Operator.(CanResponseErrors); ok { returnErrors := can.ResponseErrors() codes := map[int][]string{} - for i := range returnErrors { - e := statuserror.FromErr(returnErrors[i]) - codes[e.StatusCode()] = append(codes[e.StatusCode()], e.Summary()) + for _, err := range returnErrors { + if e, ok := err.(statuserror.ErrorWithStatusCode); ok { + s, ok := statuserror.Summary(err) + if ok { + codes[e.StatusCode()] = append(codes[e.StatusCode()], s) + } + } + } + + if op.Responses == nil { + op.Responses = map[string]*openapi.ResponseObject{} } for statusCode := range codes { - errResp := &openapi.ResponseObject{} + errResp, ok := op.Responses[fmt.Sprintf("%d", statusCode)] + if !ok { + errResp = &openapi.ResponseObject{} + } + mt := &openapi.MediaTypeObject{} mt.Schema = b.SchemaFromType( ctx, @@ -286,12 +302,16 @@ func (b *scanner) scanResponse(ctx context.Context, op *openapi.OperationObject, ) errResp.AddContent("application/json", mt) - errResp.AddExtension("x-status-return-errors", codes[statusCode]) + + if found, ok := errResp.GetExtension("x-status-return-errors"); ok { + errResp.AddExtension("x-status-return-errors", append(found.([]string), codes[statusCode]...)) + } else { + errResp.AddExtension("x-status-return-errors", codes[statusCode]) + } op.AddResponse(statusCode, errResp) } } - } type CanRuntimeDoc interface { diff --git a/pkg/courierhttp/openapi/zz_generated.runtimedoc.go b/pkg/courierhttp/openapi/zz_generated.runtimedoc.go index b3b496f..795def4 100644 --- a/pkg/courierhttp/openapi/zz_generated.runtimedoc.go +++ b/pkg/courierhttp/openapi/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package openapi GENERATED BY gengo:runtimedoc +Package openapi GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package openapi diff --git a/pkg/courierhttp/payload.go b/pkg/courierhttp/payload.go index da422e9..e1a14d3 100644 --- a/pkg/courierhttp/payload.go +++ b/pkg/courierhttp/payload.go @@ -244,10 +244,9 @@ func (r *response[T]) WriteResponse(ctx context.Context, rw http.ResponseWriter, statusErr, ok := statuserror.IsStatusErr(err) if !ok { if errors.Is(err, context.Canceled) { - // https://httpstatuses.com/499 - statusErr = statuserror.Wrap(err, 499, "ContextCanceled") + statusErr = statuserror.New(&ErrContextCanceled{Reason: err.Error()}) } else { - statusErr = statuserror.Wrap(err, http.StatusInternalServerError, "InternalServerError") + statusErr = statuserror.New(err) } } resp = statusErr.AppendSource(req.ServiceName()) diff --git a/pkg/openapi/jsonschema/extractors/zz_generated.runtimedoc.go b/pkg/openapi/jsonschema/extractors/zz_generated.runtimedoc.go index 64d07ab..895a1a8 100644 --- a/pkg/openapi/jsonschema/extractors/zz_generated.runtimedoc.go +++ b/pkg/openapi/jsonschema/extractors/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package extractors GENERATED BY gengo:runtimedoc +Package extractors GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package extractors diff --git a/pkg/openapi/jsonschema/zz_generated.deepcopy.go b/pkg/openapi/jsonschema/zz_generated.deepcopy.go index 7af45dc..d640912 100644 --- a/pkg/openapi/jsonschema/zz_generated.deepcopy.go +++ b/pkg/openapi/jsonschema/zz_generated.deepcopy.go @@ -1,5 +1,5 @@ /* -Package jsonschema GENERATED BY gengo:deepcopy +Package jsonschema GENERATED BY gengo:deepcopy DON'T EDIT THIS FILE */ package jsonschema diff --git a/pkg/openapi/jsonschema/zz_generated.runtimedoc.go b/pkg/openapi/jsonschema/zz_generated.runtimedoc.go index 5b7d969..6a9c15c 100644 --- a/pkg/openapi/jsonschema/zz_generated.runtimedoc.go +++ b/pkg/openapi/jsonschema/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package jsonschema GENERATED BY gengo:runtimedoc +Package jsonschema GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package jsonschema diff --git a/pkg/openapi/zz_generated.runtimedoc.go b/pkg/openapi/zz_generated.runtimedoc.go index 4a0f516..099bdda 100644 --- a/pkg/openapi/zz_generated.runtimedoc.go +++ b/pkg/openapi/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package openapi GENERATED BY gengo:runtimedoc +Package openapi GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package openapi diff --git a/pkg/statuserror/status_err.go b/pkg/statuserror/status_err.go index c091a43..21e137e 100644 --- a/pkg/statuserror/status_err.go +++ b/pkg/statuserror/status_err.go @@ -14,6 +14,7 @@ import ( ) type ErrorWithStatusCode interface { + Error() string StatusCode() int } @@ -34,7 +35,7 @@ func New(err error) *StatusErr { statusCode := http.StatusInternalServerError - if canStatusCode, ok := err.(interface{ StatusCode() int }); ok { + if canStatusCode, ok := err.(ErrorWithStatusCode); ok { statusCode = canStatusCode.StatusCode() } diff --git a/pkg/statuserror/status_err_summary.go b/pkg/statuserror/status_err_summary.go index 725e9b0..750a613 100644 --- a/pkg/statuserror/status_err_summary.go +++ b/pkg/statuserror/status_err_summary.go @@ -8,6 +8,21 @@ import ( "github.com/pkg/errors" ) +type ErrorWithSummary interface { + Summary() string +} + +func Summary(err error) (string, bool) { + if s, ok := err.(ErrorWithSummary); ok { + return s.Summary(), true + } + e := FromErr(err) + if e == nil { + return "", false + } + return e.Summary(), true +} + func ParseStatusErrSummary(str string) (*StatusErr, error) { s := &scanner.Scanner{} s.Init(bytes.NewBufferString(str)) diff --git a/pkg/statuserror/zz_generated.runtimedoc.go b/pkg/statuserror/zz_generated.runtimedoc.go index fd2dc82..b8f3578 100644 --- a/pkg/statuserror/zz_generated.runtimedoc.go +++ b/pkg/statuserror/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package statuserror GENERATED BY gengo:runtimedoc +Package statuserror GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package statuserror diff --git a/pkg/transformer/core/transformer.go b/pkg/transformer/core/transformer.go index 5ae0b2a..540f08f 100644 --- a/pkg/transformer/core/transformer.go +++ b/pkg/transformer/core/transformer.go @@ -87,7 +87,7 @@ func (op Option) String() string { values := url.Values{} if op.Name != "" { - values.Add("Name", op.Name) + values.Add("OrgName", op.Name) } if op.MIME != "" { diff --git a/pkg/transformer/multipart/zz_generated.runtimedoc.go b/pkg/transformer/multipart/zz_generated.runtimedoc.go index 1151fd8..9076adb 100644 --- a/pkg/transformer/multipart/zz_generated.runtimedoc.go +++ b/pkg/transformer/multipart/zz_generated.runtimedoc.go @@ -1,5 +1,5 @@ /* -Package multipart GENERATED BY gengo:runtimedoc +Package multipart GENERATED BY gengo:runtimedoc DON'T EDIT THIS FILE */ package multipart diff --git a/tool/internal/cmd/gen/main.go b/tool/internal/cmd/gen/main.go index 14a054f..25a4957 100644 --- a/tool/internal/cmd/gen/main.go +++ b/tool/internal/cmd/gen/main.go @@ -3,9 +3,6 @@ package main import ( "context" - "github.com/go-courier/logr/slog" - - "github.com/go-courier/logr" "github.com/octohelm/gengo/pkg/gengo" _ "github.com/octohelm/courier/devpkg/clientgen" @@ -30,8 +27,7 @@ func main() { panic(err) } - ctx := logr.WithLogger(context.Background(), slog.Logger(slog.Default())) - if err := c.Execute(ctx, gengo.GetRegisteredGenerators()...); err != nil { + if err := c.Execute(context.Background(), gengo.GetRegisteredGenerators()...); err != nil { panic(err) } }