diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff1b4b1..9b630a5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,4 +25,5 @@ jobs: run: chmod +x ./hack/tools.sh - name: Unit Test + if: success() && files.exists('**/*_test.go') run: make test diff --git a/common/consts/consts.go b/common/consts/consts.go new file mode 100644 index 0000000..2bd92dd --- /dev/null +++ b/common/consts/consts.go @@ -0,0 +1,109 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package consts + +const ( + HttpMethodGet = "GET" + HttpMethodPost = "POST" + HttpMethodPut = "PUT" + HttpMethodPatch = "PATCH" + HttpMethodDelete = "DELETE" + HttpMethodOptions = "OPTIONS" + HttpMethodHead = "HEAD" +) + +const ( + ApiGet = "api.get" + ApiPost = "api.post" + ApiPut = "api.put" + ApiPatch = "api.patch" + ApiDelete = "api.delete" + ApiOptions = "api.options" + ApiHEAD = "api.head" + ApiAny = "api.any" + ApiQuery = "api.query" + ApiForm = "api.form" + ApiPath = "api.path" + ApiHeader = "api.header" + ApiCookie = "api.cookie" + ApiBody = "api.body" + ApiRawBody = "api.raw_body" + ApiBaseDomain = "api.base_domain" + ApiBaseURL = "api.baseurl" + OpenapiOperation = "openapi.operation" + OpenapiProperty = "openapi.property" + OpenapiSchema = "openapi.schema" + OpenapiParameter = "openapi.parameter" + OpenapiDocument = "openapi.document" +) + +const ( + CodeGenerationCommentPbHttp = "// Code generated by protoc-gen-http-swagger." + CodeGenerationCommentThriftHttp = "// Code generated by thrift-gen-http-swagger." + CodeGenerationCommentThriftRpc = "// Code generated by thrift-gen-rpc-swagger." +) + +const ( + PluginNameProtocHttpSwagger = "protoc-gen-http-swagger" + PluginNameThriftHttpSwagger = "thrift-gen-http-swagger" + PluginNameThriftRpcSwagger = "thrift-gen-rpc-swagger" +) + +const ( + OpenAPIVersion = "3.0.3" + InfoURL = "https://github.com/hertz-contrib/swagger-generate/" + URLDefaultPrefixHTTP = "http://" + URLDefaultPrefixHTTPS = "https://" + DefaultInfoTitle = "API generated by " + DefaultInfoDesc = "API description" + DefaultInfoVersion = "0.0.1" + + DocumentOptionServiceType = "service" + DocumentOptionStructType = "struct" + + DefaultResponseDesc = "Successful response" + DefaultExceptionDesc = "Exception response" + StatusOK = "200" + StatusBadRequest = "400" + SchemaObjectType = "object" + ComponentSchemaPrefix = "#/components/schemas/" + ComponentSchemaSuffixBody = "Body" + ComponentSchemaSuffixRawBody = "RawBody" + + ContentTypeJSON = "application/json" + ContentTypeFormMultipart = "multipart/form-data" + ContentTypeFormURLEncoded = "application/x-www-form-urlencoded" + ContentTypeRawBody = "text/plain" + + ParameterInQuery = "query" + ParameterInHeader = "header" + ParameterInPath = "path" + ParameterInCookie = "cookie" + + DefaultOutputDir = "swagger" + DefaultOutputYamlFile = "openapi.yaml" + DefaultOutputSwaggerFile = "swagger.go" + + DefaultServerURL = "http://127.0.0.1:8888" + DefaultKitexAddr = "127.0.0.1:8888" + + ParameterNameTTHeader = "ttheader" + ParameterDescription = "metainfo for request" + + CommentPatternRegexp = `//\s*(.*)|/\*([\s\S]*?)\*/` + LinterRulePatternRegexp = `\(-- .* --\)` +) diff --git a/common/tpl/tpl.go b/common/tpl/tpl.go new file mode 100644 index 0000000..42d24c4 --- /dev/null +++ b/common/tpl/tpl.go @@ -0,0 +1,312 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package tpl + +const ServerTemplateHttp = `package swagger + +import ( + "context" + _ "embed" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/hertz-contrib/cors" + "github.com/hertz-contrib/swagger" + swaggerFiles "github.com/swaggo/files" +) + +//go:embed openapi.yaml +var openapiYAML []byte + +func BindSwagger(h *server.Hertz) { + h.Use(cors.Default()) + + h.GET("/swagger/*any", swagger.WrapHandler( + swaggerFiles.Handler, + swagger.URL("/openapi.yaml"), + )) + + h.GET("/openapi.yaml", func(c context.Context, ctx *app.RequestContext) { + ctx.Header("Content-Type", "application/x-yaml") + ctx.Write(openapiYAML) + }) +} +` + +const ServerTemplateRpc = `package swagger + +import ( + "context" + _ "embed" + "encoding/json" + "errors" + "fmt" + "net" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/bytedance/gopkg/cloud/metainfo" + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/cloudwego/hertz/pkg/network" + "github.com/cloudwego/hertz/pkg/route" + "github.com/cloudwego/kitex/client" + "github.com/cloudwego/kitex/client/genericclient" + "github.com/cloudwego/kitex/pkg/endpoint" + "github.com/cloudwego/kitex/pkg/generic" + "github.com/cloudwego/kitex/pkg/klog" + "github.com/cloudwego/kitex/pkg/remote" + "github.com/cloudwego/kitex/pkg/remote/trans/detection" + "github.com/cloudwego/kitex/pkg/remote/trans/netpoll" + "github.com/cloudwego/kitex/pkg/remote/trans/nphttp2" + "github.com/cloudwego/kitex/pkg/transmeta" + "github.com/cloudwego/kitex/transport" + "github.com/hertz-contrib/cors" + "github.com/hertz-contrib/swagger" + swaggerFiles "github.com/swaggo/files" +) + +var ( + //go:embed openapi.yaml + openapiYAML []byte + hertzEngine *route.Engine + httpReg = regexp.MustCompile("^(?:GET |POST|PUT|DELE|HEAD|OPTI|CONN|TRAC|PATC)$") +) + +const ( + kitexAddr = "{{.KitexAddr}}" + idlFile = "{{.IdlPath}}" +) + +type MixTransHandlerFactory struct { + OriginFactory remote.ServerTransHandlerFactory +} + +type transHandler struct { + remote.ServerTransHandler +} + +func (t *transHandler) SetInvokeHandleFunc(inkHdlFunc endpoint.Endpoint) { + t.ServerTransHandler.(remote.InvokeHandleFuncSetter).SetInvokeHandleFunc(inkHdlFunc) +} + +func (m MixTransHandlerFactory) NewTransHandler(opt *remote.ServerOption) (remote.ServerTransHandler, error) { + + if hertzEngine == nil { + StartServer() + } + + var kitexOrigin remote.ServerTransHandler + var err error + + if m.OriginFactory != nil { + kitexOrigin, err = m.OriginFactory.NewTransHandler(opt) + } else { + kitexOrigin, err = detection.NewSvrTransHandlerFactory(netpoll.NewSvrTransHandlerFactory(), nphttp2.NewSvrTransHandlerFactory()).NewTransHandler(opt) + } + if err != nil { + return nil, err + } + return &transHandler{ServerTransHandler: kitexOrigin}, nil +} + +func (t *transHandler) OnRead(ctx context.Context, conn net.Conn) error { + c, ok := conn.(network.Conn) + if ok { + pre, _ := c.Peek(4) + if httpReg.Match(pre) { + klog.Info("using Hertz to process request") + err := hertzEngine.Serve(ctx, c) + if err != nil { + err = errors.New(fmt.Sprintf("HERTZ: %s", err.Error())) + } + return err + } + } + + return t.ServerTransHandler.OnRead(ctx, conn) +} + +func StartServer() { + h := server.Default() + h.Use(cors.Default()) + + cli := initializeGenericClient() + setupSwaggerRoutes(h) + setupProxyRoutes(h, cli) + + hlog.Info("Swagger UI is available at: http://" + kitexAddr + "/swagger/index.html") + err := h.Engine.Init() + if err != nil { + panic(err) + } + + hertzEngine = h.Engine +} + +func findThriftFile(fileName string) (string, error) { + workingDir, err := os.Getwd() + if err != nil { + return "", err + } + + foundPath := "" + err = filepath.Walk(workingDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && info.Name() == fileName { + foundPath = path + return filepath.SkipDir + } + return nil + }) + + if err == nil && foundPath != "" { + return foundPath, nil + } + + parentDir := filepath.Dir(workingDir) + for parentDir != "/" && parentDir != "." && parentDir != workingDir { + filePath := filepath.Join(parentDir, fileName) + if _, err := os.Stat(filePath); err == nil { + return filePath, nil + } + workingDir = parentDir + parentDir = filepath.Dir(parentDir) + } + + return "", errors.New("thrift file not found: " + fileName) +} + +func initializeGenericClient() genericclient.Client { + thriftFile, err := findThriftFile(idlFile) + if err != nil { + hlog.Fatal("Failed to locate Thrift file:", err) + } + + p, err := generic.NewThriftFileProviderWithDynamicGo(thriftFile) + if err != nil { + hlog.Fatal("Failed to create ThriftFileProvider:", err) + } + + g, err := generic.JSONThriftGeneric(p) + if err != nil { + hlog.Fatal("Failed to create HTTPThriftGeneric:", err) + } + var opts []client.Option + opts = append(opts, client.WithTransportProtocol(transport.TTHeader)) + opts = append(opts, client.WithMetaHandler(transmeta.ClientTTHeaderHandler)) + opts = append(opts, client.WithHostPorts(kitexAddr)) + cli, err := genericclient.NewClient("swagger", g, opts...) + if err != nil { + hlog.Fatal("Failed to create generic client:", err) + } + + return cli +} + +func setupSwaggerRoutes(h *server.Hertz) { + h.GET("swagger/*any", swagger.WrapHandler(swaggerFiles.Handler, swagger.URL("/openapi.yaml"))) + + h.GET("/openapi.yaml", func(c context.Context, ctx *app.RequestContext) { + ctx.Header("Content-Type", "application/x-yaml") + ctx.Write(openapiYAML) + }) +} + +func setupProxyRoutes(h *server.Hertz, cli genericclient.Client) { + h.Any("/*ServiceMethod", func(c context.Context, ctx *app.RequestContext) { + serviceMethod := ctx.Param("ServiceMethod") + if serviceMethod == "" { + handleError(ctx, "ServiceMethod not provided", http.StatusBadRequest) + return + } + + bodyBytes := ctx.Request.Body() + + queryMap := formatQueryParams(ctx) + + for k, v := range queryMap { + if strings.HasPrefix(k, "p_") { + c = metainfo.WithPersistentValue(c, k, v) + } else { + c = metainfo.WithValue(c, k, v) + } + } + + c = metainfo.WithBackwardValues(c) + + jReq := string(bodyBytes) + + jRsp, err := cli.GenericCall(c, serviceMethod, jReq) + if err != nil { + hlog.Errorf("GenericCall error: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": err.Error(), + }) + return + } + + result := make(map[string]interface{}) + if err := json.Unmarshal([]byte(jRsp.(string)), &result); err != nil { + hlog.Errorf("Failed to unmarshal response body: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": "Failed to unmarshal response body", + }) + return + } + + m := metainfo.RecvAllBackwardValues(c) + + for key, value := range m { + result[key] = value + } + + respBody, err := json.Marshal(result) + if err != nil { + hlog.Errorf("Failed to marshal response body: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": "Failed to marshal response body", + }) + return + } + + ctx.Data(http.StatusOK, "application/json", respBody) + + }) +} + +func formatQueryParams(ctx *app.RequestContext) map[string]string { + var QueryParams = make(map[string]string) + ctx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) { + QueryParams[string(key)] = string(value) + }) + return QueryParams +} + +func handleError(ctx *app.RequestContext, errMsg string, statusCode int) { + hlog.Errorf("Error: %s", errMsg) + ctx.JSON(statusCode, map[string]interface{}{ + "error": errMsg, + }) +} +` diff --git a/common/utils/utils.go b/common/utils/utils.go new file mode 100644 index 0000000..e38512a --- /dev/null +++ b/common/utils/utils.go @@ -0,0 +1,164 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "errors" + "fmt" + "os" + "reflect" + "strconv" + "strings" +) + +// Contains returns true if an array Contains a specified string. +func Contains(s []string, e string) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +func UnpackArgs(args []string, c interface{}) error { + m, err := MapForm(args) + if err != nil { + return fmt.Errorf("unmarshal args failed, err: %v", err.Error()) + } + + t := reflect.TypeOf(c).Elem() + v := reflect.ValueOf(c).Elem() + if t.Kind() != reflect.Struct { + return errors.New("passed c must be struct or pointer of struct") + } + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + x := v.Field(i) + n := f.Name + values, ok := m[n] + if !ok || len(values) == 0 || values[0] == "" { + continue + } + switch x.Kind() { + case reflect.Bool: + if len(values) != 1 { + return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) + } + x.SetBool(values[0] == "true") + case reflect.String: + if len(values) != 1 { + return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) + } + x.SetString(values[0]) + case reflect.Slice: + if len(values) != 1 { + return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) + } + ss := strings.Split(values[0], ";") + if x.Type().Elem().Kind() == reflect.Int { + n := reflect.MakeSlice(x.Type(), len(ss), len(ss)) + for i, s := range ss { + val, err := strconv.ParseInt(s, 10, 64) + if err != nil { + return err + } + n.Index(i).SetInt(val) + } + x.Set(n) + } else { + for _, s := range ss { + val := reflect.Append(x, reflect.ValueOf(s)) + x.Set(val) + } + } + case reflect.Map: + if len(values) != 1 { + return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) + } + ss := strings.Split(values[0], ";") + out := make(map[string]string, len(ss)) + for _, s := range ss { + sk := strings.SplitN(s, "=", 2) + if len(sk) != 2 { + return fmt.Errorf("map filed %v invalid key-value pair '%v'", n, s) + } + out[sk[0]] = sk[1] + } + x.Set(reflect.ValueOf(out)) + default: + return fmt.Errorf("field %s has unsupported type %+v", n, f.Type) + } + } + return nil +} + +func MapForm(input []string) (map[string][]string, error) { + out := make(map[string][]string, len(input)) + + for _, str := range input { + parts := strings.SplitN(str, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid argument: '%s'", str) + } + key, val := parts[0], parts[1] + out[key] = append(out[key], val) + } + + return out, nil +} + +// MergeStructs merges non-zero fields from src into dst. +func MergeStructs(dst, src interface{}) error { + dstVal := reflect.ValueOf(dst) + srcVal := reflect.ValueOf(src) + + // Ensure both dst and src are pointers to structs. + if dstVal.Kind() != reflect.Ptr || srcVal.Kind() != reflect.Ptr { + return errors.New("both dst and src must be pointers") + } + if dstVal.Elem().Kind() != reflect.Struct || srcVal.Elem().Kind() != reflect.Struct { + return errors.New("both dst and src must be pointers to structs") + } + + dstVal = dstVal.Elem() + srcVal = srcVal.Elem() + + for i := 0; i < dstVal.NumField(); i++ { + field := dstVal.Field(i) + srcField := srcVal.Field(i) + + if !srcField.IsZero() { + field.Set(srcField) + } + } + + return nil +} + +func AppendUnique(s []string, e string) []string { + if !Contains(s, e) { + return append(s, e) + } + return s +} + +func FileExists(filePath string) bool { + _, err := os.Stat(filePath) + return err == nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3a3e045 --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/hertz-contrib/swagger-generate + +go 1.18 + +require ( + github.com/apache/thrift v0.13.0 + github.com/google/gnostic-models v0.6.8 + google.golang.org/protobuf v1.34.2 + gopkg.in/yaml.v3 v3.0.1 +) + +require github.com/golang/protobuf v1.5.4 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..31e1cfc --- /dev/null +++ b/go.sum @@ -0,0 +1,14 @@ +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/protoc-gen-http-swagger/protobuf/api/api.pb.go b/idl/protobuf/api/api.pb.go similarity index 100% rename from protoc-gen-http-swagger/protobuf/api/api.pb.go rename to idl/protobuf/api/api.pb.go diff --git a/protoc-gen-http-swagger/protobuf/api/api.proto b/idl/protobuf/api/api.proto similarity index 100% rename from protoc-gen-http-swagger/protobuf/api/api.proto rename to idl/protobuf/api/api.proto diff --git a/protoc-gen-http-swagger/protobuf/api/http_option.go b/idl/protobuf/api/http_option.go similarity index 100% rename from protoc-gen-http-swagger/protobuf/api/http_option.go rename to idl/protobuf/api/http_option.go diff --git a/protoc-gen-http-swagger/protobuf/openapi/annotations.pb.go b/idl/protobuf/openapi/annotations.pb.go similarity index 100% rename from protoc-gen-http-swagger/protobuf/openapi/annotations.pb.go rename to idl/protobuf/openapi/annotations.pb.go diff --git a/protoc-gen-http-swagger/protobuf/openapi/annotations.proto b/idl/protobuf/openapi/annotations.proto similarity index 100% rename from protoc-gen-http-swagger/protobuf/openapi/annotations.proto rename to idl/protobuf/openapi/annotations.proto diff --git a/protoc-gen-http-swagger/protobuf/openapi/document.go b/idl/protobuf/openapi/document.go similarity index 100% rename from protoc-gen-http-swagger/protobuf/openapi/document.go rename to idl/protobuf/openapi/document.go diff --git a/protoc-gen-http-swagger/protobuf/openapi/openapi.pb.go b/idl/protobuf/openapi/openapi.pb.go similarity index 100% rename from protoc-gen-http-swagger/protobuf/openapi/openapi.pb.go rename to idl/protobuf/openapi/openapi.pb.go diff --git a/protoc-gen-http-swagger/protobuf/openapi/openapi.proto b/idl/protobuf/openapi/openapi.proto similarity index 100% rename from protoc-gen-http-swagger/protobuf/openapi/openapi.proto rename to idl/protobuf/openapi/openapi.proto diff --git a/thrift-gen-http-swagger/thrift/document.go b/idl/thrift/document.go similarity index 100% rename from thrift-gen-http-swagger/thrift/document.go rename to idl/thrift/document.go diff --git a/thrift-gen-http-swagger/thrift/openapi.go b/idl/thrift/openapi.go similarity index 99% rename from thrift-gen-http-swagger/thrift/openapi.go rename to idl/thrift/openapi.go index bf28471..6ca483a 100644 --- a/thrift-gen-http-swagger/thrift/openapi.go +++ b/idl/thrift/openapi.go @@ -20,7 +20,6 @@ package openapi import ( "fmt" - "github.com/apache/thrift/lib/go/thrift" ) diff --git a/idl/thrift/openapi.thrift b/idl/thrift/openapi.thrift new file mode 100644 index 0000000..95b320d --- /dev/null +++ b/idl/thrift/openapi.thrift @@ -0,0 +1,562 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace go openapi + +struct _ServiceOptions { + 1:required Document document +} + +struct _StructOptions { + 1:required Schema schema +} + +struct _MethodOptions { + 1:required Operation operation +} + +struct _FieldOptions { + 1:required Parameter parameter + 2:required Schema property +} + +struct AdditionalPropertiesItem { + 1: SchemaOrReference schema_or_reference, + 2: bool boolean +} + +struct Any { + 1: _Any value, + 2: string yaml +} + +struct _Any { + 1: string type_url, + 2: binary value +} + +struct AnyOrExpression { + 1: Any any, + 2: Expression expression +} + +struct Callback { + 1: list path, + 2: list specification_extension +} + +struct CallbackOrReference { + 1: Callback callback, + 2: Reference reference +} + +struct CallbacksOrReferences { + 1: list additional_properties +} + +struct Components { + 1: SchemasOrReferences schemas, + 2: ResponsesOrReferences responses, + 3: ParametersOrReferences parameters, + 4: ExamplesOrReferences examples, + 5: RequestBodiesOrReferences request_bodies, + 6: HeadersOrReferences headers, + 7: SecuritySchemesOrReferences security_schemes, + 8: LinksOrReferences links, + 9: CallbacksOrReferences callbacks, + 10: list specification_extension +} + +struct Contact { + 1: string name, + 2: string url, + 3: string email, + 4: list specification_extension +} + +struct DefaultType { + 1: double number, + 2: bool boolean, + 3: string string +} + +struct Discriminator { + 1: string property_name, + 2: Strings mapping, + 3: list specification_extension +} + +struct Document { + 1: string openapi, + 2: Info info, + 3: list servers, + 4: Paths paths, + 5: Components components, + 6: list security, + 7: list tags, + 8: ExternalDocs external_docs, + 9: list specification_extension +} + +struct Encoding { + 1: string content_type, + 2: HeadersOrReferences headers, + 3: string style, + 4: bool explode, + 5: bool allow_reserved, + 6: list specification_extension +} + +struct Encodings { + 1: list additional_properties +} + +struct Example { + 1: string summary, + 2: string description, + 3: Any value, + 4: string external_value, + 5: list specification_extension +} + +struct ExampleOrReference { + 1: Example example, + 2: Reference reference +} + +struct ExamplesOrReferences { + 1: list additional_properties +} + +struct Expression { + 1: list additional_properties +} + +struct ExternalDocs { + 1: string description, + 2: string url, + 3: list specification_extension +} + +struct Header { + 1: string description, + 2: bool required, + 3: bool deprecated, + 4: bool allow_empty_value, + 5: string style, + 6: bool explode, + 7: bool allow_reserved, + 8: SchemaOrReference schema, + 9: Any example, + 10: ExamplesOrReferences examples, + 11: MediaTypes content, + 12: list specification_extension +} + +struct HeaderOrReference { + 1: Header header, + 2: Reference reference +} + +struct HeadersOrReferences { + 1: list additional_properties +} + +struct Info { + 1: string title, + 2: string description, + 3: string terms_of_service, + 4: Contact contact, + 5: License license, + 6: string version, + 7: list specification_extension, + 8: string summary +} + +struct ItemsItem { + 1: list schema_or_reference +} + +struct License { + 1: string name, + 2: string url, + 3: list specification_extension +} + +struct Link { + 1: string operation_ref, + 2: string operation_id, + 3: AnyOrExpression parameters, + 4: AnyOrExpression request_body, + 5: string description, + 6: Server server, + 7: list specification_extension +} + +struct LinkOrReference { + 1: Link link, + 2: Reference reference +} + +struct LinksOrReferences { + 1: list additional_properties +} + +struct MediaType { + 1: SchemaOrReference schema, + 2: Any example, + 3: ExamplesOrReferences examples, + 4: Encodings encoding, + 5: list specification_extension +} + +struct MediaTypes { + 1: list additional_properties +} + +struct NamedAny { + 1: string name, + 2: Any value +} + +struct NamedCallbackOrReference { + 1: string name, + 2: CallbackOrReference value +} + +struct NamedEncoding { + 1: string name, + 2: Encoding value +} + +struct NamedExampleOrReference { + 1: string name, + 2: ExampleOrReference value +} + +struct NamedHeaderOrReference { + 1: string name, + 2: HeaderOrReference value +} + +struct NamedLinkOrReference { + 1: string name, + 2: LinkOrReference value +} + +struct NamedMediaType { + 1: string name, + 2: MediaType value +} + +struct NamedParameterOrReference { + 1: string name, + 2: ParameterOrReference value +} + +struct NamedPathItem { + 1: string name, + 2: PathItem value +} + +struct NamedRequestBodyOrReference { + 1: string name, + 2: RequestBodyOrReference value +} + +struct NamedResponseOrReference { + 1: string name, + 2: ResponseOrReference value +} + +struct NamedSchemaOrReference { + 1: string name, + 2: SchemaOrReference value +} + +struct NamedSecuritySchemeOrReference { + 1: string name, + 2: SecuritySchemeOrReference value +} + +struct NamedServerVariable { + 1: string name, + 2: ServerVariable value +} + +struct NamedString { + 1: string name, + 2: string value +} + +struct NamedStringArray { + 1: string name, + 2: StringArray value +} + +struct OauthFlow { + 1: string authorization_url, + 2: string token_url, + 3: string refresh_url, + 4: Strings scopes, + 5: list specification_extension +} + +struct OauthFlows { + 1: OauthFlow implicit, + 2: OauthFlow password, + 3: OauthFlow client_credentials, + 4: OauthFlow authorization_code, + 5: list specification_extension +} + +struct Object { + 1: list additional_properties +} + +struct Operation { + 1: list tags, + 2: string summary, + 3: string description, + 4: ExternalDocs external_docs, + 5: string operation_id, + 6: list parameters, + 7: RequestBodyOrReference request_body, + 8: Responses responses, + 9: CallbacksOrReferences callbacks, + 10: bool deprecated, + 11: list security, + 12: list servers, + 13: list specification_extension +} + +struct Parameter { + 1: string name, + 2: string in, + 3: string description, + 4: bool required, + 5: bool deprecated, + 6: bool allow_empty_value, + 7: string style, + 8: bool explode, + 9: bool allow_reserved, + 10: SchemaOrReference schema, + 11: Any example, + 12: ExamplesOrReferences examples, + 13: MediaTypes content, + 14: list specification_extension +} + +struct ParameterOrReference { + 1: Parameter parameter, + 2: Reference reference +} + +struct ParametersOrReferences { + 1: list additional_properties +} + +struct PathItem { + 1: string xref, + 2: string summary, + 3: string description, + 4: Operation get, + 5: Operation put, + 6: Operation post, + 7: Operation delete, + 8: Operation options, + 9: Operation head, + 10: Operation patch, + 11: Operation trace, + 12: list servers, + 13: list parameters, + 14: list specification_extension +} + +struct Paths { + 1: list path + 2: list specification_extension +} + +struct Properties { + 1: list additional_properties +} + +struct Reference { + 1: string xref + 2: string summary + 3: string description +} + +struct RequestBody { + 1: string description, + 2: MediaTypes content, + 3: bool required, + 4: list specification_extension +} + +struct RequestBodyOrReference { + 1: RequestBody request_body, + 2: Reference reference +} + +struct RequestBodiesOrReferences { + 1: list additional_properties +} + +struct Response { + 1: string description, + 2: HeadersOrReferences headers, + 3: MediaTypes content, + 4: LinksOrReferences links, + 5: list specification_extension +} + +struct ResponseOrReference { + 1: Response response, + 2: Reference reference +} + +struct Responses { + 1: ResponseOrReference default, + 2: list response_or_reference, + 3: list specification_extension +} + +struct ResponsesOrReferences { + 1: list additional_properties +} + +struct Schema { + 1: bool nullable, + 2: Discriminator discriminator, + 3: bool read_only, + 4: bool write_only, + 5: Xml xml, + 6: ExternalDocs external_docs, + 7: Any example, + 8: bool deprecated, + 9: string title, + 10: double multiple_of, + 11: double maximum, + 12: bool exclusive_maximum, + 13: double minimum, + 14: bool exclusive_minimum, + 15: i64 max_length, + 16: i64 min_length, + 17: string pattern, + 18: i64 max_items, + 19: i64 min_items, + 20: bool unique_items, + 21: i64 max_properties, + 22: i64 min_properties, + 23: list required, + 24: list enum, + 25: string type, + 26: list all_of, + 27: list one_of, + 28: list any_of, + 29: Schema not, + 30: ItemsItem items, + 31: Properties properties, + 32: AdditionalPropertiesItem additional_properties, + 33: DefaultType default, + 34: string description, + 35: string format, + 36: list specification_extension +} + +struct SchemaOrReference { + 1: Schema schema, + 2: Reference reference +} + +struct SchemasOrReferences { + 1: list additional_properties +} + +struct SecurityRequirement { + 1: list additional_properties +} + +struct SecurityScheme { + 1: string _type, + 2: string description, + 3: string name, + 4: string _in, + 5: string scheme, + 6: string bearer_format, + 7: OauthFlows flows, + 8: string open_id_connect_url, + 9: list specification_extension +} + +struct SecuritySchemeOrReference { + 1: SecurityScheme security_scheme, + 2: Reference reference +} + +struct SecuritySchemesOrReferences { + 1: list additional_properties +} + +struct Server { + 1: string url, + 2: string description, + 3: ServerVariables variables, + 4: list specification_extension +} + +struct ServerVariable { + 1: string _default, + 2: list enum, + 3: string description, + 4: list specification_extension +} + +struct ServerVariables { + 1: list additional_properties +} + +struct SpecificationExtension { + 1: double number, + 2: bool boolean, + 3: string string +} + +struct StringArray { + 1: list values +} + +struct Strings { + 1: list additional_properties +} + +struct Tag { + 1: string name, + 2: string description, + 3: ExternalDocs external_docs, + 4: list specification_extension +} + +struct Xml { + 1: string name, + 2: string namespace, + 3: string prefix, + 4: bool attribute, + 5: bool wrapped, + 6: list specification_extension +} \ No newline at end of file diff --git a/licenses/LICENSE-cors.txt b/licenses/LICENSE-cors.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/licenses/LICENSE-cors.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/LICENSE-swagger.txt b/licenses/LICENSE-swagger.txt new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/licenses/LICENSE-swagger.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/licenses/LICENSE-swaggo-files.txt b/licenses/LICENSE-swaggo-files.txt new file mode 100644 index 0000000..64ef3d0 --- /dev/null +++ b/licenses/LICENSE-swaggo-files.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Swaggo + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/protoc-gen-http-swagger/README.md b/protoc-gen-http-swagger/README.md index 8527994..b99f21d 100644 --- a/protoc-gen-http-swagger/README.md +++ b/protoc-gen-http-swagger/README.md @@ -96,6 +96,12 @@ protoc-gen-http-swagger --version protoc --http-swagger_out=doc -I idl hello.proto ``` +### Bind Swagger Service to Enable Swagger UI in Hertz Server + +```sh +swagger.BindSwagger(r) +``` + ## More info See [examples](example/idl/hello.proto) \ No newline at end of file diff --git a/protoc-gen-http-swagger/README_CN.md b/protoc-gen-http-swagger/README_CN.md index dfaec6a..91a8c02 100644 --- a/protoc-gen-http-swagger/README_CN.md +++ b/protoc-gen-http-swagger/README_CN.md @@ -93,8 +93,16 @@ protoc-gen-http-swagger --version ## 使用 +### 生成 swagger 文档 + +```sh +protoc --http-swagger_out=swagger -I idl hello.proto +``` + +### 在 Hertz Server 中绑定 swagger 服务开启 swagger-ui + ```sh -protoc --http-swagger_out=doc -I idl hello.proto +swagger.BindSwagger(r) ``` ## 更多信息 diff --git a/protoc-gen-http-swagger/example/doc/openapi.yaml b/protoc-gen-http-swagger/example/swagger/openapi.yaml similarity index 100% rename from protoc-gen-http-swagger/example/doc/openapi.yaml rename to protoc-gen-http-swagger/example/swagger/openapi.yaml diff --git a/protoc-gen-http-swagger/example/swagger/swagger.go b/protoc-gen-http-swagger/example/swagger/swagger.go new file mode 100644 index 0000000..5c8dd51 --- /dev/null +++ b/protoc-gen-http-swagger/example/swagger/swagger.go @@ -0,0 +1,46 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by protoc-gen-http-swagger. +package swagger + +import ( + "context" + _ "embed" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/hertz-contrib/cors" + "github.com/hertz-contrib/swagger" + swaggerFiles "github.com/swaggo/files" +) + +//go:embed openapi.yaml +var openapiYAML []byte + +func BindSwagger(h *server.Hertz) { + h.Use(cors.Default()) + + h.GET("/swagger/*any", swagger.WrapHandler( + swaggerFiles.Handler, + swagger.URL("/openapi.yaml"), + )) + + h.GET("/openapi.yaml", func(c context.Context, ctx *app.RequestContext) { + ctx.Header("Content-Type", "application/x-yaml") + ctx.Write(openapiYAML) + }) +} diff --git a/protoc-gen-http-swagger/generator/generator.go b/protoc-gen-http-swagger/generator/openapi_gen.go similarity index 88% rename from protoc-gen-http-swagger/generator/generator.go rename to protoc-gen-http-swagger/generator/openapi_gen.go index ecc1820..deac497 100644 --- a/protoc-gen-http-swagger/generator/generator.go +++ b/protoc-gen-http-swagger/generator/openapi_gen.go @@ -35,14 +35,16 @@ package generator import ( "fmt" - "log" "regexp" "sort" "strings" + "github.com/cloudwego/hertz/cmd/hz/util/logs" + "github.com/hertz-contrib/swagger-generate/common/consts" + common "github.com/hertz-contrib/swagger-generate/common/utils" + "github.com/hertz-contrib/swagger-generate/idl/protobuf/api" + "github.com/hertz-contrib/swagger-generate/idl/protobuf/openapi" wk "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/generator/wellknown" - "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/protobuf/api" - "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/protobuf/openapi" "google.golang.org/genproto/googleapis/api/annotations" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" @@ -60,10 +62,6 @@ type Configuration struct { OutputMode *string } -const ( - infoURL = "https://github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger" -) - // In order to dynamically add google.rpc.Status responses we need // to know the message descriptors for google.rpc.Status as well // as google.protobuf.Any. @@ -73,30 +71,28 @@ var ( // OpenAPIGenerator holds internal state needed to generate an OpenAPIv3 document for a transcoded Protocol Buffer service. type OpenAPIGenerator struct { - conf Configuration - plugin *protogen.Plugin - inputFiles []*protogen.File - reflect *OpenAPIReflector - generatedSchemas []string // Names of schemas that have already been generated. - linterRulePattern *regexp.Regexp + conf Configuration + plugin *protogen.Plugin + inputFiles []*protogen.File + reflect *OpenAPIReflector + generatedSchemas []string // Names of schemas that have already been generated. } // NewOpenAPIGenerator creates a new generator for a protoc plugin invocation. func NewOpenAPIGenerator(plugin *protogen.Plugin, conf Configuration, inputFiles []*protogen.File) *OpenAPIGenerator { return &OpenAPIGenerator{ - conf: conf, - plugin: plugin, - inputFiles: inputFiles, - reflect: NewOpenAPIReflector(conf), - generatedSchemas: make([]string, 0), - linterRulePattern: regexp.MustCompile(`\(-- .* --\)`), + conf: conf, + plugin: plugin, + inputFiles: inputFiles, + reflect: NewOpenAPIReflector(conf), + generatedSchemas: make([]string, 0), } } // Run runs the generator. func (g *OpenAPIGenerator) Run(outputFile *protogen.GeneratedFile) error { d := g.buildDocument() - bytes, err := d.YAMLValue("Generated with protoc-gen-http-swagger\n" + infoURL) + bytes, err := d.YAMLValue("Generated with protoc-gen-http-swagger\n" + consts.InfoURL + consts.PluginNameProtocHttpSwagger) if err != nil { return fmt.Errorf("failed to marshal yaml: %s", err.Error()) } @@ -110,7 +106,7 @@ func (g *OpenAPIGenerator) Run(outputFile *protogen.GeneratedFile) error { func (g *OpenAPIGenerator) buildDocument() *openapi.Document { d := &openapi.Document{} - d.Openapi = "3.0.3" + d.Openapi = consts.OpenAPIVersion d.Info = &openapi.Info{ Version: *g.conf.Version, Title: *g.conf.Title, @@ -135,7 +131,7 @@ func (g *OpenAPIGenerator) buildDocument() *openapi.Document { if doc, ok := extDocument.(*openapi.Document); ok { proto.Merge(d, doc) } else { - log.Printf("unexpected type for Document: %T", extDocument) + logs.Errorf("unexpected type for Document: %T", extDocument) } } g.addPathsToDocument(d, file.Services) @@ -171,24 +167,24 @@ func (g *OpenAPIGenerator) buildDocument() *openapi.Document { var servers []string // Only 1 server will ever be set, per method, by the generator if path.Value.Get != nil && len(path.Value.Get.Servers) == 1 { - servers = appendUnique(servers, path.Value.Get.Servers[0].Url) - allServers = appendUnique(allServers, path.Value.Get.Servers[0].Url) + servers = common.AppendUnique(servers, path.Value.Get.Servers[0].Url) + allServers = common.AppendUnique(allServers, path.Value.Get.Servers[0].Url) } if path.Value.Post != nil && len(path.Value.Post.Servers) == 1 { - servers = appendUnique(servers, path.Value.Post.Servers[0].Url) - allServers = appendUnique(allServers, path.Value.Post.Servers[0].Url) + servers = common.AppendUnique(servers, path.Value.Post.Servers[0].Url) + allServers = common.AppendUnique(allServers, path.Value.Post.Servers[0].Url) } if path.Value.Put != nil && len(path.Value.Put.Servers) == 1 { - servers = appendUnique(servers, path.Value.Put.Servers[0].Url) - allServers = appendUnique(allServers, path.Value.Put.Servers[0].Url) + servers = common.AppendUnique(servers, path.Value.Put.Servers[0].Url) + allServers = common.AppendUnique(allServers, path.Value.Put.Servers[0].Url) } if path.Value.Delete != nil && len(path.Value.Delete.Servers) == 1 { - servers = appendUnique(servers, path.Value.Delete.Servers[0].Url) - allServers = appendUnique(allServers, path.Value.Delete.Servers[0].Url) + servers = common.AppendUnique(servers, path.Value.Delete.Servers[0].Url) + allServers = common.AppendUnique(allServers, path.Value.Delete.Servers[0].Url) } if path.Value.Patch != nil && len(path.Value.Patch.Servers) == 1 { - servers = appendUnique(servers, path.Value.Patch.Servers[0].Url) - allServers = appendUnique(allServers, path.Value.Patch.Servers[0].Url) + servers = common.AppendUnique(servers, path.Value.Patch.Servers[0].Url) + allServers = common.AppendUnique(allServers, path.Value.Patch.Servers[0].Url) } if len(servers) == 1 { @@ -255,7 +251,7 @@ func (g *OpenAPIGenerator) buildDocument() *openapi.Document { // filterCommentString removes linter rules from comments. func (g *OpenAPIGenerator) filterCommentString(c protogen.Comments) string { - comment := g.linterRulePattern.ReplaceAllString(string(c), "") + comment := regexp.MustCompile(consts.LinterRulePatternRegexp).ReplaceAllString(string(c), "") return strings.TrimSpace(comment) } @@ -277,7 +273,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputMessage *protogen.Message, bod var required []string for _, field := range inputMessage.Fields { if ext := proto.GetExtension(field.Desc.Options(), bodyType); ext != "" { - if contains(allRequired, ext.(string)) { + if common.Contains(allRequired, ext.(string)) { required = append(required, ext.(string)) } @@ -301,7 +297,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputMessage *protogen.Message, bod } } default: - log.Printf("unsupported extension type %T", extension) + logs.Error("unsupported extension type %T", extension) } } @@ -347,7 +343,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputMessage *protogen.Message, bod } schema := &openapi.Schema{ - Type: "object", + Type: consts.SchemaObjectType, Properties: definitionProperties, } @@ -384,7 +380,7 @@ func (g *OpenAPIGenerator) buildOperation( // Check for each type of extension (query, path, cookie, header) if ext = proto.GetExtension(field.Desc.Options(), api.E_Query); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Query).(string) - paramIn = "query" + paramIn = consts.ParameterInQuery paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -394,13 +390,13 @@ func (g *OpenAPIGenerator) buildOperation( if property, ok := extProperty.(*openapi.Schema); ok { proto.Merge(schema.Schema, property) } else { - log.Printf("unexpected type for Property: %T", extProperty) + logs.Errorf("unexpected type for Property: %T", extProperty) } } } } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Path); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Path).(string) - paramIn = "path" + paramIn = consts.ParameterInPath paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -414,7 +410,7 @@ func (g *OpenAPIGenerator) buildOperation( required = true } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Cookie); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Cookie).(string) - paramIn = "cookie" + paramIn = consts.ParameterInCookie paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -426,7 +422,7 @@ func (g *OpenAPIGenerator) buildOperation( } } else if ext = proto.GetExtension(field.Desc.Options(), api.E_Header); ext != "" { paramName = proto.GetExtension(field.Desc.Options(), api.E_Header).(string) - paramIn = "header" + paramIn = consts.ParameterInHeader paramDesc = g.filterCommentString(field.Comments.Leading) fieldSchema = g.reflect.schemaOrReferenceForField(field.Desc) if schema, ok := fieldSchema.Oneof.(*openapi.SchemaOrReference_Schema); ok { @@ -449,7 +445,7 @@ func (g *OpenAPIGenerator) buildOperation( if parameterExt, ok := extParameter.(*openapi.Parameter); ok { proto.Merge(parameter, parameterExt) } else { - log.Printf("unexpected type for Parameter: %T", extParameter) + logs.Errorf("unexpected type for Parameter: %T", extParameter) } } @@ -464,7 +460,7 @@ func (g *OpenAPIGenerator) buildOperation( } var RequestBody *openapi.RequestBodyOrReference - if methodName != "GET" && methodName != "HEAD" && methodName != "DELETE" { + if methodName != consts.HttpMethodGet && methodName != consts.HttpMethodHead && methodName != consts.HttpMethodDelete { bodySchema := g.getSchemaByOption(inputMessage, api.E_Body) formSchema := g.getSchemaByOption(inputMessage, api.E_Form) rawBodySchema := g.getSchemaByOption(inputMessage, api.E_RawBody) @@ -473,7 +469,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(bodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: consts.ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -486,7 +482,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(formSchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "multipart/form-data", + Name: consts.ContentTypeFormMultipart, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -497,7 +493,7 @@ func (g *OpenAPIGenerator) buildOperation( }) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/x-www-form-urlencoded", + Name: consts.ContentTypeFormURLEncoded, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -510,7 +506,7 @@ func (g *OpenAPIGenerator) buildOperation( if len(rawBodySchema.Properties.AdditionalProperties) > 0 { additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: consts.ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Schema{ @@ -539,7 +535,7 @@ func (g *OpenAPIGenerator) buildOperation( desc := g.filterCommentString(outputMessage.Comments.Leading) if desc == "" { - desc = "Successful response" + desc = consts.DefaultResponseDesc } var headerOrEmpty *openapi.HeadersOrReferences @@ -582,8 +578,8 @@ func (g *OpenAPIGenerator) buildOperation( RequestBody: RequestBody, } if defaultHost != "" { - if !strings.HasPrefix(defaultHost, "http://") && !strings.HasPrefix(defaultHost, "https://") { - defaultHost = "http://" + defaultHost + if !strings.HasPrefix(defaultHost, consts.URLDefaultPrefixHTTP) && !strings.HasPrefix(defaultHost, consts.URLDefaultPrefixHTTPS) { + defaultHost = consts.URLDefaultPrefixHTTP + defaultHost } op.Servers = append(op.Servers, &openapi.Server{Url: defaultHost}) } @@ -620,13 +616,13 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p if len(bodySchema.Properties.AdditionalProperties) > 0 { refSchema := &openapi.NamedSchemaOrReference{ - Name: g.reflect.formatMessageName(message.Desc) + "Body", + Name: g.reflect.formatMessageName(message.Desc) + consts.ComponentSchemaSuffixBody, Value: &openapi.SchemaOrReference{Oneof: &openapi.SchemaOrReference_Schema{Schema: bodySchema}}, } - ref := "#/components/schemas/" + g.reflect.formatMessageName(message.Desc) + "Body" + ref := consts.ComponentSchemaPrefix + g.reflect.formatMessageName(message.Desc) + consts.ComponentSchemaSuffixBody g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: consts.ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Reference{ @@ -639,13 +635,13 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p if len(rawBodySchema.Properties.AdditionalProperties) > 0 { refSchema := &openapi.NamedSchemaOrReference{ - Name: g.reflect.formatMessageName(message.Desc) + "RawBody", + Name: g.reflect.formatMessageName(message.Desc) + consts.ComponentSchemaSuffixRawBody, Value: &openapi.SchemaOrReference{Oneof: &openapi.SchemaOrReference_Schema{Schema: rawBodySchema}}, } - ref := "#/components/schemas/" + g.reflect.formatMessageName(message.Desc) + "RawBody" + ref := consts.ComponentSchemaPrefix + g.reflect.formatMessageName(message.Desc) + consts.ComponentSchemaSuffixRawBody g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: consts.ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Oneof: &openapi.SchemaOrReference_Reference{ @@ -660,7 +656,7 @@ func (g *OpenAPIGenerator) getResponseForMessage(d *openapi.Document, message *p AdditionalProperties: additionalProperties, } - return "200", headers, content + return consts.StatusOK, headers, content } // addOperationToDocument adds an operation to the specified path/method. @@ -679,19 +675,19 @@ func (g *OpenAPIGenerator) addOperationToDocument(d *openapi.Document, op *opena } // Set the operation on the specified method. switch methodName { - case "GET": + case consts.HttpMethodGet: selectedPathItem.Value.Get = op - case "POST": + case consts.HttpMethodPost: selectedPathItem.Value.Post = op - case "PUT": + case consts.HttpMethodPut: selectedPathItem.Value.Put = op - case "DELETE": + case consts.HttpMethodDelete: selectedPathItem.Value.Delete = op - case "PATCH": + case consts.HttpMethodPatch: selectedPathItem.Value.Patch = op - case "OPTIONS": + case consts.HttpMethodOptions: selectedPathItem.Value.Options = op - case "HEAD": + case consts.HttpMethodHead: selectedPathItem.Value.Head = op } } @@ -735,7 +731,7 @@ func (g *OpenAPIGenerator) addPathsToDocument(d *openapi.Document, services []*p // addSchemaToDocument adds the schema to the document if required func (g *OpenAPIGenerator) addSchemaToDocument(d *openapi.Document, schema *openapi.NamedSchemaOrReference) { - if contains(g.generatedSchemas, schema.Name) { + if common.Contains(g.generatedSchemas, schema.Name) { return } g.generatedSchemas = append(g.generatedSchemas, schema.Name) @@ -753,8 +749,8 @@ func (g *OpenAPIGenerator) addSchemasForMessagesToDocument(d *openapi.Document, schemaName := g.reflect.formatMessageName(message.Desc) // Only generate this if we need it and haven't already generated it. - if !contains(g.reflect.requiredSchemas, schemaName) || - contains(g.generatedSchemas, schemaName) { + if !common.Contains(g.reflect.requiredSchemas, schemaName) || + common.Contains(g.generatedSchemas, schemaName) { continue } @@ -803,7 +799,7 @@ func (g *OpenAPIGenerator) addSchemasForMessagesToDocument(d *openapi.Document, } } default: - log.Printf("unsupported extension type %T", extension) + logs.Error("unsupported extension type %T", extension) } } @@ -860,7 +856,7 @@ func (g *OpenAPIGenerator) addSchemasForMessagesToDocument(d *openapi.Document, } schema := &openapi.Schema{ - Type: "object", + Type: consts.SchemaObjectType, Description: messageDescription, Properties: definitionProperties, Required: required, diff --git a/protoc-gen-http-swagger/generator/reflector.go b/protoc-gen-http-swagger/generator/reflector.go index c8360c4..5f1d214 100644 --- a/protoc-gen-http-swagger/generator/reflector.go +++ b/protoc-gen-http-swagger/generator/reflector.go @@ -37,8 +37,10 @@ import ( "log" "strings" + common "github.com/hertz-contrib/swagger-generate/common/utils" + "github.com/hertz-contrib/swagger-generate/idl/protobuf/openapi" wk "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/generator/wellknown" - "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/protobuf/openapi" + "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/utils" "google.golang.org/protobuf/reflect/protoreflect" ) @@ -116,7 +118,7 @@ func (r *OpenAPIReflector) fullMessageTypeName(message protoreflect.MessageDescr func (r *OpenAPIReflector) schemaReferenceForMessage(message protoreflect.MessageDescriptor) string { schemaName := r.formatMessageName(message) - if !contains(r.requiredSchemas, schemaName) { + if !common.Contains(r.requiredSchemas, schemaName) { r.requiredSchemas = append(r.requiredSchemas, schemaName) } return "#/components/schemas/" + schemaName @@ -161,13 +163,13 @@ func (r *OpenAPIReflector) schemaOrReferenceForMessage(message protoreflect.Mess return wk.NewBytesSchema() case ".google.protobuf.Int32Value", ".google.protobuf.UInt32Value": - return wk.NewIntegerSchema(getValueKind(message)) + return wk.NewIntegerSchema(utils.GetValueKind(message)) case ".google.protobuf.StringValue", ".google.protobuf.Int64Value", ".google.protobuf.UInt64Value": return wk.NewStringSchema() case ".google.protobuf.FloatValue", ".google.protobuf.DoubleValue": - return wk.NewNumberSchema(getValueKind(message)) + return wk.NewNumberSchema(utils.GetValueKind(message)) default: ref := r.schemaReferenceForMessage(message) diff --git a/protoc-gen-http-swagger/generator/server_gen.go b/protoc-gen-http-swagger/generator/server_gen.go new file mode 100644 index 0000000..6acf91c --- /dev/null +++ b/protoc-gen-http-swagger/generator/server_gen.go @@ -0,0 +1,78 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package generator + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "text/template" + + "github.com/hertz-contrib/swagger-generate/common/consts" + "github.com/hertz-contrib/swagger-generate/common/tpl" + "github.com/hertz-contrib/swagger-generate/common/utils" + "google.golang.org/protobuf/compiler/protogen" +) + +type ServerGenerator struct { + IdlPath string +} + +func NewServerGenerator(inputFiles []*protogen.File) (*ServerGenerator, error) { + var idlPath string + var genFiles []*protogen.File + for _, f := range inputFiles { + if f.Generate { + genFiles = append(genFiles, f) + } + } + if len(genFiles) > 1 { + return nil, errors.New("only one .proto file is supported for generation swagger") + } else if len(genFiles) == 1 { + idlPath = genFiles[0].Desc.Path() + } else { + return nil, errors.New("no .proto files marked for generation") + } + + return &ServerGenerator{ + IdlPath: idlPath, + }, nil +} + +func (g *ServerGenerator) Generate(outputFile *protogen.GeneratedFile) error { + filePath := filepath.Join(filepath.Dir(g.IdlPath), consts.DefaultOutputSwaggerFile) + if utils.FileExists(filePath) { + return errors.New("swagger.go file already exists") + } + + tmpl, err := template.New("server").Delims("{{", "}}").Parse(consts.CodeGenerationCommentPbHttp + "\n" + tpl.ServerTemplateHttp) + if err != nil { + return fmt.Errorf("failed to parse template: %w", err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, g) + if err != nil { + return fmt.Errorf("failed to execute template: %w", err) + } + + if _, err = outputFile.Write(buf.Bytes()); err != nil { + return fmt.Errorf("failed to write output file: %v", err) + } + return nil +} diff --git a/protoc-gen-http-swagger/generator/wellknown/schemas.go b/protoc-gen-http-swagger/generator/wellknown/schemas.go index 929ff4e..47ded85 100644 --- a/protoc-gen-http-swagger/generator/wellknown/schemas.go +++ b/protoc-gen-http-swagger/generator/wellknown/schemas.go @@ -34,7 +34,7 @@ package wellknown import ( - v3 "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/protobuf/openapi" + v3 "github.com/hertz-contrib/swagger-generate/idl/protobuf/openapi" "google.golang.org/protobuf/reflect/protoreflect" ) diff --git a/protoc-gen-http-swagger/go.mod b/protoc-gen-http-swagger/go.mod index 3786c94..0266c86 100644 --- a/protoc-gen-http-swagger/go.mod +++ b/protoc-gen-http-swagger/go.mod @@ -1,17 +1,51 @@ module github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger -go 1.22 +go 1.18 require ( - github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 + github.com/cloudwego/hertz v0.9.3 + github.com/cloudwego/hertz/cmd/hz v0.9.1 + github.com/hertz-contrib/cors v0.1.0 + github.com/hertz-contrib/swagger v0.1.0 + github.com/hertz-contrib/swagger-generate v0.0.0 + github.com/swaggo/files v1.0.1 google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 google.golang.org/protobuf v1.34.2 - gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect + github.com/bytedance/gopkg v0.1.0 // indirect + github.com/bytedance/sonic v1.12.0 // indirect + github.com/bytedance/sonic/loader v0.2.0 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect + github.com/cloudwego/netpoll v0.6.2 // indirect + github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/google/go-cmp v0.5.9 // indirect - github.com/kr/pretty v0.3.1 // indirect - github.com/rogpeppe/go-internal v1.11.0 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + github.com/henrylee2cn/ameda v1.4.10 // indirect + github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/nyaruka/phonenumbers v1.0.55 // indirect + github.com/swaggo/swag v1.16.1 // indirect + github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.24.0 // indirect + golang.org/x/tools v0.11.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/hertz-contrib/swagger-generate => ../../swagger-generate diff --git a/protoc-gen-http-swagger/go.sum b/protoc-gen-http-swagger/go.sum index 096a777..2777742 100644 --- a/protoc-gen-http-swagger/go.sum +++ b/protoc-gen-http-swagger/go.sum @@ -1,25 +1,302 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs= +github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94zf2JZ3X4= +github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls= +github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/hertz v0.6.2/go.mod h1:2em2hGREvCBawsTQcQxyWBGVlCeo+N1pp2q0HkkbwR0= +github.com/cloudwego/hertz v0.9.3 h1:uajvLn6LjEPjUqN/ewUZtWoRQWa2es2XTELdqDlOYMw= +github.com/cloudwego/hertz v0.9.3/go.mod h1:gGVUfJU/BOkJv/ZTzrw7FS7uy7171JeYIZvAyV3wS3o= +github.com/cloudwego/hertz/cmd/hz v0.9.1 h1:v75TueFIZhTgYYnoM+6VxKHu58ZS3HJ+Qp4T07UYcKk= +github.com/cloudwego/hertz/cmd/hz v0.9.1/go.mod h1:6SroAwvZkyL54CiPANDkTR3YoX2MY4ZOW1+gtmWhRJE= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.6.2 h1:+KdILv5ATJU+222wNNXpHapYaBeRvvL8qhJyhcxRxrQ= +github.com/cloudwego/netpoll v0.6.2/go.mod h1:kaqvfZ70qd4T2WtIIpCOi5Cxyob8viEpzLhCrTrz3HM= +github.com/cloudwego/thriftgo v0.1.7/go.mod h1:LzeafuLSiHA9JTiWC8TIMIq64iadeObgRUhmVG1OC/w= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/hertz-contrib/cors v0.1.0 h1:PQ5mATygSMzTlYtfyMyHjobYoJeHKe2Qt3tcAOgbI6E= +github.com/hertz-contrib/cors v0.1.0/go.mod h1:VPReoq+Rvu/lZOfpp5CcX3x4mpZUc3EpSXBcVDcbvOc= +github.com/hertz-contrib/swagger v0.1.0 h1:FlnMPRHuvAt/3pt3KCQRZ6RH1g/agma9SU70Op2Pb58= +github.com/hertz-contrib/swagger v0.1.0/go.mod h1:Bt5i+Nyo7bGmYbuEfMArx7raf1oK+nWVgYbEvhpICKE= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= +github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= +github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= +github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/urfave/cli/v2 v2.23.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 h1:0+ozOGcrp+Y8Aq8TLNN2Aliibms5LEzsq99ZZmAGYm0= google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094/go.mod h1:fJ/e3If/Q67Mj99hin0hMhiNyCRmt6BQ2aWIJshUSJw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/protoc-gen-http-swagger/main.go b/protoc-gen-http-swagger/main.go index bd8afa0..246e818 100644 --- a/protoc-gen-http-swagger/main.go +++ b/protoc-gen-http-swagger/main.go @@ -38,6 +38,7 @@ import ( "path/filepath" "strings" + "github.com/hertz-contrib/swagger-generate/common/consts" "github.com/hertz-contrib/swagger-generate/protoc-gen-http-swagger/generator" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/types/pluginpb" @@ -68,7 +69,7 @@ func main() { if !file.Generate { continue } - outfileName := strings.TrimSuffix(file.Desc.Path(), filepath.Ext(file.Desc.Path())) + ".openapi.yaml" + outfileName := strings.TrimSuffix(file.Desc.Path(), filepath.Ext(file.Desc.Path())) + "." + consts.DefaultOutputYamlFile outputFile := plugin.NewGeneratedFile(outfileName, "") gen := generator.NewOpenAPIGenerator(plugin, conf, []*protogen.File{file}) if err := gen.Run(outputFile); err != nil { @@ -76,8 +77,19 @@ func main() { } } } else { - outputFile := plugin.NewGeneratedFile("openapi.yaml", "") - return generator.NewOpenAPIGenerator(plugin, conf, plugin.Files).Run(outputFile) + outputFile := plugin.NewGeneratedFile(consts.DefaultOutputYamlFile, "") + gen := generator.NewOpenAPIGenerator(plugin, conf, plugin.Files) + if err := gen.Run(outputFile); err != nil { + return err + } + } + outputFile := plugin.NewGeneratedFile("swagger.go", "") + gen, err := generator.NewServerGenerator(plugin.Files) + if err != nil { + return err + } + if err = gen.Generate(outputFile); err != nil { + return err } return nil }) diff --git a/protoc-gen-http-swagger/generator/utils.go b/protoc-gen-http-swagger/utils/utils.go similarity index 77% rename from protoc-gen-http-swagger/generator/utils.go rename to protoc-gen-http-swagger/utils/utils.go index 37ea7e8..614b548 100644 --- a/protoc-gen-http-swagger/generator/utils.go +++ b/protoc-gen-http-swagger/utils/utils.go @@ -31,31 +31,13 @@ * Modifications are Copyright 2024 CloudWeGo Authors. */ -package generator +package utils import ( "google.golang.org/protobuf/reflect/protoreflect" ) -// contains returns true if an array contains a specified string. -func contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} - -// appendUnique appends a string, to a string slice, if the string is not already in the slice -func appendUnique(s []string, e string) []string { - if !contains(s, e) { - return append(s, e) - } - return s -} - -func getValueKind(message protoreflect.MessageDescriptor) string { +func GetValueKind(message protoreflect.MessageDescriptor) string { valueField := getValueField(message) return valueField.Kind().String() } diff --git a/thrift-gen-http-swagger/README.md b/thrift-gen-http-swagger/README.md index 36b1534..d1bc5f7 100644 --- a/thrift-gen-http-swagger/README.md +++ b/thrift-gen-http-swagger/README.md @@ -93,9 +93,13 @@ thrift-gen-http-swagger --version ## Usage ```sh +thriftgo -g go -p http-swagger hello.thrift +``` -thriftgo -g go -p http-swagger:OutputDir={swagger documentation output directory} {idl}.thrift +### Bind Swagger Service to Enable Swagger UI in Hertz Server +```sh +swagger.BindSwagger(r) ``` ## More info diff --git a/thrift-gen-http-swagger/README_CN.md b/thrift-gen-http-swagger/README_CN.md index 92cd173..5213925 100644 --- a/thrift-gen-http-swagger/README_CN.md +++ b/thrift-gen-http-swagger/README_CN.md @@ -94,9 +94,13 @@ thrift-gen-http-swagger --version ## 使用 ```sh +thriftgo -g go -p http-swagger hello.thrift +``` -thriftgo -g go -p http-swagger:OutputDir={swagger文档输出目录} {idl}.thrift +### 在 Hertz Server 中绑定 swagger 服务开启 swagger-ui +```sh +swagger.BindSwagger(r) ``` ## 更多信息 diff --git a/thrift-gen-http-swagger/args/argument.go b/thrift-gen-http-swagger/args/argument.go index ccba49d..3610b6f 100644 --- a/thrift-gen-http-swagger/args/argument.go +++ b/thrift-gen-http-swagger/args/argument.go @@ -19,7 +19,7 @@ package args import ( "fmt" - "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/utils" + "github.com/hertz-contrib/swagger-generate/common/utils" ) type Arguments struct { diff --git a/thrift-gen-http-swagger/example/docs/openapi.yaml b/thrift-gen-http-swagger/example/swagger/openapi.yaml similarity index 100% rename from thrift-gen-http-swagger/example/docs/openapi.yaml rename to thrift-gen-http-swagger/example/swagger/openapi.yaml diff --git a/thrift-gen-http-swagger/example/swagger/swagger.go b/thrift-gen-http-swagger/example/swagger/swagger.go new file mode 100644 index 0000000..e559a31 --- /dev/null +++ b/thrift-gen-http-swagger/example/swagger/swagger.go @@ -0,0 +1,46 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by thrift-gen-http-swagger. +package swagger + +import ( + "context" + _ "embed" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/hertz-contrib/cors" + "github.com/hertz-contrib/swagger" + swaggerFiles "github.com/swaggo/files" +) + +//go:embed openapi.yaml +var openapiYAML []byte + +func BindSwagger(h *server.Hertz) { + h.Use(cors.Default()) + + h.GET("/swagger/*any", swagger.WrapHandler( + swaggerFiles.Handler, + swagger.URL("/openapi.yaml"), + )) + + h.GET("/openapi.yaml", func(c context.Context, ctx *app.RequestContext) { + ctx.Header("Content-Type", "application/x-yaml") + ctx.Write(openapiYAML) + }) +} diff --git a/thrift-gen-http-swagger/generator/generator.go b/thrift-gen-http-swagger/generator/openapi_gen.go similarity index 54% rename from thrift-gen-http-swagger/generator/generator.go rename to thrift-gen-http-swagger/generator/openapi_gen.go index 67a934a..0d37651 100644 --- a/thrift-gen-http-swagger/generator/generator.go +++ b/thrift-gen-http-swagger/generator/openapi_gen.go @@ -34,7 +34,6 @@ package generator import ( - "fmt" "path/filepath" "regexp" "sort" @@ -44,45 +43,40 @@ import ( "github.com/cloudwego/thriftgo/parser" "github.com/cloudwego/thriftgo/plugin" "github.com/cloudwego/thriftgo/thrift_reflection" + "github.com/hertz-contrib/swagger-generate/common/consts" + common "github.com/hertz-contrib/swagger-generate/common/utils" + openapi "github.com/hertz-contrib/swagger-generate/idl/thrift" "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/args" - openapi "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/thrift" "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/utils" ) -const ( - infoURL = "https://github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger" -) - type OpenAPIGenerator struct { - fileDesc *thrift_reflection.FileDescriptor - ast *parser.Thrift - generatedSchemas []string - requiredSchemas []string - commentPattern *regexp.Regexp - linterRulePattern *regexp.Regexp + fileDesc *thrift_reflection.FileDescriptor + ast *parser.Thrift + generatedSchemas []string + requiredSchemas []string + requiredTypeDesc []*thrift_reflection.StructDescriptor } // NewOpenAPIGenerator creates a new generator for a protoc plugin invocation. func NewOpenAPIGenerator(ast *parser.Thrift) *OpenAPIGenerator { _, fileDesc := thrift_reflection.RegisterAST(ast) return &OpenAPIGenerator{ - fileDesc: fileDesc, - ast: ast, - generatedSchemas: make([]string, 0), - commentPattern: regexp.MustCompile(`//\s*(.*)|/\*([\s\S]*?)\*/`), - linterRulePattern: regexp.MustCompile(`\(-- .* --\)`), + fileDesc: fileDesc, + ast: ast, + generatedSchemas: make([]string, 0), } } func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Generated { d := &openapi.Document{} - version := "3.0.3" + version := consts.OpenAPIVersion d.Openapi = version d.Info = &openapi.Info{ - Title: "API generated by thrift-gen-http-swagger", - Description: "API description", - Version: "1.0.0", + Title: consts.DefaultInfoTitle + consts.PluginNameThriftHttpSwagger, + Description: consts.DefaultInfoDesc, + Version: consts.DefaultInfoVersion, } d.Paths = &openapi.Paths{} d.Components = &openapi.Components{ @@ -94,18 +88,22 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge var extDocument *openapi.Document err := g.getDocumentOption(&extDocument) if err != nil { - fmt.Printf("Error getting document option: %s\n", err) + logs.Errorf("Error merging document option: %s", err) return nil } if extDocument != nil { - utils.MergeStructs(d, extDocument) + err := common.MergeStructs(d, extDocument) + if err != nil { + logs.Errorf("Error merging document option: %s", err) + return nil + } } - g.addPathsToDocument(d, g.ast.Services) + g.addPathsToDocument(d, g.fileDesc.GetServices()) for len(g.requiredSchemas) > 0 { count := len(g.requiredSchemas) - g.addSchemasForStructsToDocument(d, g.ast.GetStructLikes()) + g.addSchemasForStructsToDocument(d, g.requiredTypeDesc) g.requiredSchemas = g.requiredSchemas[count:len(g.requiredSchemas)] } @@ -116,7 +114,6 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge if d.Info.Description == "" { d.Info.Description = d.Tags[0].Description } - d.Tags[0].Description = "" } var allServers []string @@ -126,24 +123,24 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge var servers []string // Only 1 server will ever be set, per method, by the generator if path.Value.Get != nil && len(path.Value.Get.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Get.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Get.Servers[0].URL) + servers = common.AppendUnique(servers, path.Value.Get.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Get.Servers[0].URL) } if path.Value.Post != nil && len(path.Value.Post.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Post.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Post.Servers[0].URL) + servers = common.AppendUnique(servers, path.Value.Post.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Post.Servers[0].URL) } if path.Value.Put != nil && len(path.Value.Put.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Put.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Put.Servers[0].URL) + servers = common.AppendUnique(servers, path.Value.Put.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Put.Servers[0].URL) } if path.Value.Delete != nil && len(path.Value.Delete.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Delete.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Delete.Servers[0].URL) + servers = common.AppendUnique(servers, path.Value.Delete.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Delete.Servers[0].URL) } if path.Value.Patch != nil && len(path.Value.Patch.Servers) == 1 { - servers = utils.AppendUnique(servers, path.Value.Patch.Servers[0].URL) - allServers = utils.AppendUnique(allServers, path.Value.Patch.Servers[0].URL) + servers = common.AppendUnique(servers, path.Value.Patch.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Patch.Servers[0].URL) } if len(servers) == 1 { @@ -206,13 +203,16 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge d.Components.Schemas.AdditionalProperties = pairs } - bytes, err := d.YAMLValue("Generated with thrift-gen-http-swagger\n" + infoURL) + bytes, err := d.YAMLValue("Generated with " + consts.PluginNameThriftHttpSwagger + "\n" + consts.InfoURL + consts.PluginNameThriftHttpSwagger) if err != nil { - fmt.Printf("Error converting to yaml: %s\n", err) + logs.Errorf("Error converting to yaml: %s", err) return nil } - filePath := filepath.Clean(arguments.OutputDir) - filePath = filepath.Join(filePath, "openapi.yaml") + outputDir := arguments.OutputDir + if outputDir == "" { + outputDir = consts.DefaultOutputDir + } + filePath := filepath.Join(outputDir, consts.DefaultOutputYamlFile) var ret []*plugin.Generated ret = append(ret, &plugin.Generated{ Content: string(bytes), @@ -224,15 +224,15 @@ func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Ge func (g *OpenAPIGenerator) getDocumentOption(obj interface{}) error { serviceOrStruct, name := g.getDocumentAnnotationInWhichServiceOrStruct() - if serviceOrStruct == "service" { + if serviceOrStruct == consts.DocumentOptionServiceType { serviceDesc := g.fileDesc.GetServiceDescriptor(name) - err := utils.ParseServiceOption(serviceDesc, OpenapiDocument, obj) + err := utils.ParseServiceOption(serviceDesc, consts.OpenapiDocument, obj) if err != nil { return err } - } else if serviceOrStruct == "struct" { + } else if serviceOrStruct == consts.DocumentOptionStructType { structDesc := g.fileDesc.GetStructDescriptor(name) - err := utils.ParseStructOption(structDesc, OpenapiDocument, obj) + err := utils.ParseStructOption(structDesc, consts.OpenapiDocument, obj) if err != nil { return err } @@ -240,56 +240,82 @@ func (g *OpenAPIGenerator) getDocumentOption(obj interface{}) error { return nil } -func (g *OpenAPIGenerator) addPathsToDocument(d *openapi.Document, services []*parser.Service) { +func (g *OpenAPIGenerator) addPathsToDocument(d *openapi.Document, services []*thrift_reflection.ServiceDescriptor) { + var err error for _, s := range services { annotationsCount := 0 - for _, f := range s.Functions { - comment := g.filterCommentString(f.ReservedComments) - operationID := s.GetName() + "_" + f.GetName() - rs := utils.GetAnnotations(f.Annotations, HttpMethodAnnotations) + for _, m := range s.GetMethods() { + var inputDesc, outputDesc, throwDesc *thrift_reflection.StructDescriptor + + rs := utils.GetAnnotations(m.Annotations, HttpMethodAnnotations) if len(rs) == 0 { continue } - var inputDesc *thrift_reflection.StructDescriptor - if len(f.Arguments) >= 1 { - if len(f.Arguments) > 1 { - logs.Warnf("function '%s' has more than one argument, but only the first can be used in hertz now", f.GetName()) + if len(m.Args) > 0 { + if len(m.Args) > 1 { + logs.Warnf("function '%s' has more than one argument, but only the first can be used in plugin now", m.GetName()) + } + // TODO: support more argument types + if m.Args[0].GetType().IsStruct() { + inputDesc, err = m.Args[0].GetType().GetStructDescriptor() + if err != nil { + logs.Errorf("Error getting arguments descriptor: %s", err) + } + } else { + logs.Errorf("now only support struct type for input, but got %s", m.Args[0].GetType().GetName()) + } + } + + // TODO: support more response types + if m.Response.IsStruct() { + outputDesc, err = m.Response.GetStructDescriptor() + if err != nil { + logs.Errorf("Error getting response descriptor: %s", err) } - inputDesc = g.fileDesc.GetStructDescriptor(f.GetArguments()[0].GetType().GetName()) + } else if m.Response.Name != "void" { + logs.Errorf("now only support struct type for output, but got %s", m.Response.Name) } - outputDesc := g.fileDesc.GetStructDescriptor(f.GetFunctionType().GetName()) + + if len(m.ThrowExceptions) > 0 { + throwDesc, err = m.ThrowExceptions[0].GetType().GetExceptionDescriptor() + if err != nil { + logs.Errorf("Error getting exception descriptor: %s", err) + } + } + for methodName, path := range rs { if methodName != "" { - annotationsCount++ var host string - hostOrNil := utils.GetAnnotation(f.Annotations, ApiBaseURL) - if len(hostOrNil) != 0 { - host = utils.GetAnnotation(f.Annotations, ApiBaseURL)[0] + if urls, ok := m.Annotations[consts.ApiBaseURL]; ok && len(urls) > 0 { + host = urls[0] + } else if domains, ok := s.Annotations[consts.ApiBaseDomain]; ok && len(domains) > 0 { + host = domains[0] } - if host == "" { - hostOrNil = utils.GetAnnotation(s.Annotations, ApiBaseDomain) - if len(hostOrNil) != 0 { - host = utils.GetAnnotation(s.Annotations, ApiBaseDomain)[0] - } - } + annotationsCount++ + operationID := s.GetName() + "_" + m.GetName() + comment := g.filterCommentString(m.Comments) + + op, path2 := g.buildOperation(d, methodName, comment, operationID, s.GetName(), path[0], host, inputDesc, outputDesc, throwDesc) - op, path2 := g.buildOperation(d, methodName, comment, operationID, s.GetName(), path[0], host, inputDesc, outputDesc) - methodDesc := g.fileDesc.GetMethodDescriptor(s.GetName(), f.GetName()) newOp := &openapi.Operation{} - err := utils.ParseMethodOption(methodDesc, OpenapiOperation, &newOp) + err = utils.ParseMethodOption(m, consts.OpenapiOperation, &newOp) if err != nil { logs.Errorf("Error parsing method option: %s", err) } - utils.MergeStructs(op, newOp) + err = common.MergeStructs(op, newOp) + if err != nil { + logs.Errorf("Error merging method option: %s", err) + } + g.addOperationToDocument(d, op, path2, methodName) } } } if annotationsCount > 0 { - comment := g.filterCommentString(s.ReservedComments) + comment := g.filterCommentString(s.Comments) d.Tags = append(d.Tags, &openapi.Tag{Name: s.GetName(), Description: comment}) } } @@ -305,6 +331,7 @@ func (g *OpenAPIGenerator) buildOperation( host string, inputDesc *thrift_reflection.StructDescriptor, outputDesc *thrift_reflection.StructDescriptor, + throwDesc *thrift_reflection.StructDescriptor, ) (*openapi.Operation, string) { // Parameters array to hold all parameter objects var parameters []*openapi.ParameterOrReference @@ -314,76 +341,76 @@ func (g *OpenAPIGenerator) buildOperation( var fieldSchema *openapi.SchemaOrReference required := false - extOrNil := v.Annotations[ApiQuery] + extOrNil := v.Annotations[consts.ApiQuery] if len(extOrNil) > 0 { - if ext := v.Annotations[ApiQuery][0]; ext != "" { - paramIn = "query" + if ext := v.Annotations[consts.ApiQuery][0]; ext != "" { + paramIn = consts.ParameterInQuery paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) - extPropertyOrNil := v.Annotations[OpenapiProperty] + extPropertyOrNil := v.Annotations[consts.OpenapiProperty] if len(extPropertyOrNil) > 0 { newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(v, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(v, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + common.MergeStructs(fieldSchema.Schema, newFieldSchema) } } } - extOrNil = v.Annotations[ApiPath] + extOrNil = v.Annotations[consts.ApiPath] if len(extOrNil) > 0 { - if ext := v.Annotations[ApiPath][0]; ext != "" { - paramIn = "path" + if ext := v.Annotations[consts.ApiPath][0]; ext != "" { + paramIn = consts.ParameterInPath paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) - extPropertyOrNil := v.Annotations[OpenapiProperty] + extPropertyOrNil := v.Annotations[consts.OpenapiProperty] if len(extPropertyOrNil) > 0 { newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(v, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(v, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + common.MergeStructs(fieldSchema.Schema, newFieldSchema) } required = true } } - extOrNil = v.Annotations[ApiCookie] + extOrNil = v.Annotations[consts.ApiCookie] if len(extOrNil) > 0 { - if ext := v.Annotations[ApiCookie][0]; ext != "" { - paramIn = "cookie" + if ext := v.Annotations[consts.ApiCookie][0]; ext != "" { + paramIn = consts.ParameterInCookie paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) - extPropertyOrNil := v.Annotations[OpenapiProperty] + extPropertyOrNil := v.Annotations[consts.OpenapiProperty] if len(extPropertyOrNil) > 0 { newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(v, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(v, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + common.MergeStructs(fieldSchema.Schema, newFieldSchema) } } } - extOrNil = v.Annotations[ApiHeader] + extOrNil = v.Annotations[consts.ApiHeader] if len(extOrNil) > 0 { - if ext := v.Annotations[ApiHeader][0]; ext != "" { - paramIn = "header" + if ext := v.Annotations[consts.ApiHeader][0]; ext != "" { + paramIn = consts.ParameterInHeader paramName = ext paramDesc = g.filterCommentString(v.Comments) fieldSchema = g.schemaOrReferenceForField(v.Type) - extPropertyOrNil := v.Annotations[OpenapiProperty] + extPropertyOrNil := v.Annotations[consts.OpenapiProperty] if len(extPropertyOrNil) > 0 { newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(v, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(v, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + common.MergeStructs(fieldSchema.Schema, newFieldSchema) } } } @@ -397,11 +424,11 @@ func (g *OpenAPIGenerator) buildOperation( } var extParameter *openapi.Parameter - err := utils.ParseFieldOption(v, OpenapiParameter, &extParameter) + err := utils.ParseFieldOption(v, consts.OpenapiParameter, &extParameter) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(parameter, extParameter) + common.MergeStructs(parameter, extParameter) // Append the parameter to the parameters array if it was set if paramName != "" && paramIn != "" { @@ -412,101 +439,89 @@ func (g *OpenAPIGenerator) buildOperation( } var RequestBody *openapi.RequestBodyOrReference - if methodName != "GET" && methodName != "HEAD" && methodName != "DELETE" { - bodySchema := g.getSchemaByOption(inputDesc, ApiBody) - formSchema := g.getSchemaByOption(inputDesc, ApiForm) - rawBodySchema := g.getSchemaByOption(inputDesc, ApiRawBody) - - var additionalProperties []*openapi.NamedMediaType - if len(bodySchema.Properties.AdditionalProperties) > 0 { - additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", - Value: &openapi.MediaType{ - Schema: &openapi.SchemaOrReference{ - Schema: bodySchema, - }, - }, - }) - } - if len(formSchema.Properties.AdditionalProperties) > 0 { - additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "multipart/form-data", - Value: &openapi.MediaType{ - Schema: &openapi.SchemaOrReference{ - Schema: formSchema, + if inputDesc != nil { + if methodName != consts.HttpMethodGet && methodName != consts.HttpMethodHead && methodName != consts.HttpMethodDelete { + bodySchema := g.getSchemaByOption(inputDesc, consts.ApiBody) + formSchema := g.getSchemaByOption(inputDesc, consts.ApiForm) + rawBodySchema := g.getSchemaByOption(inputDesc, consts.ApiRawBody) + + var additionalProperties []*openapi.NamedMediaType + if len(bodySchema.Properties.AdditionalProperties) > 0 { + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeJSON, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Schema: bodySchema, + }, }, - }, - }) + }) + } - additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/x-www-form-urlencoded", - Value: &openapi.MediaType{ - Schema: &openapi.SchemaOrReference{ - Schema: formSchema, + if len(formSchema.Properties.AdditionalProperties) > 0 { + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeFormMultipart, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Schema: formSchema, + }, }, - }, - }) - } + }) - if len(rawBodySchema.Properties.AdditionalProperties) > 0 { - additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", - Value: &openapi.MediaType{ - Schema: &openapi.SchemaOrReference{ - Schema: rawBodySchema, + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeFormURLEncoded, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Schema: formSchema, + }, }, - }, - }) - } + }) + } - if len(additionalProperties) > 0 { - RequestBody = &openapi.RequestBodyOrReference{ - RequestBody: &openapi.RequestBody{ - Description: g.filterCommentString(inputDesc.Comments), - Content: &openapi.MediaTypes{ - AdditionalProperties: additionalProperties, + if len(rawBodySchema.Properties.AdditionalProperties) > 0 { + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeRawBody, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Schema: rawBodySchema, + }, }, - }, + }) } - } - - } - - name, header, content := g.getResponseForStruct(d, outputDesc) - desc := g.filterCommentString(outputDesc.Comments) - if desc == "" { - desc = "Successful response" - } - - var headerOrEmpty *openapi.HeadersOrReferences + if len(additionalProperties) > 0 { + RequestBody = &openapi.RequestBodyOrReference{ + RequestBody: &openapi.RequestBody{ + Description: g.filterCommentString(inputDesc.Comments), + Content: &openapi.MediaTypes{ + AdditionalProperties: additionalProperties, + }, + }, + } + } - if len(header.AdditionalProperties) != 0 { - headerOrEmpty = header + } } - var contentOrEmpty *openapi.MediaTypes + var responses *openapi.Responses - if len(content.AdditionalProperties) != 0 { - contentOrEmpty = content + if outputDesc != nil { + response := g.processResponse(d, outputDesc, consts.StatusOK) + if response != nil { + if responses == nil { + responses = &openapi.Responses{} + } + responses.ResponseOrReference = append(responses.ResponseOrReference, response) + } } - var responses *openapi.Responses - if headerOrEmpty != nil || contentOrEmpty != nil { - responses = &openapi.Responses{ - ResponseOrReference: []*openapi.NamedResponseOrReference{ - { - Name: name, - Value: &openapi.ResponseOrReference{ - Response: &openapi.Response{ - Description: desc, - Headers: headerOrEmpty, - Content: contentOrEmpty, - }, - }, - }, - }, + if throwDesc != nil { + response := g.processResponse(d, throwDesc, consts.StatusBadRequest) + if response != nil { + if responses == nil { + responses = &openapi.Responses{} + } + responses.ResponseOrReference = append(responses.ResponseOrReference, response) } } @@ -523,8 +538,8 @@ func (g *OpenAPIGenerator) buildOperation( } if host != "" { - if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") { - host = "http://" + host + if !strings.HasPrefix(host, consts.URLDefaultPrefixHTTP) && !strings.HasPrefix(host, consts.URLDefaultPrefixHTTPS) { + host = consts.URLDefaultPrefixHTTP + host } op.Servers = append(op.Servers, &openapi.Server{URL: host}) } @@ -532,33 +547,71 @@ func (g *OpenAPIGenerator) buildOperation( return op, path } +func (g *OpenAPIGenerator) processResponse(d *openapi.Document, desc *thrift_reflection.StructDescriptor, statusCode string) *openapi.NamedResponseOrReference { + name, header, content := g.getResponseForStruct(d, desc, statusCode) + description := g.filterCommentString(desc.Comments) + + if description == "" { + if statusCode == consts.StatusOK { + description = consts.DefaultResponseDesc + } else { + description = consts.DefaultExceptionDesc + } + } + + var headerOrEmpty *openapi.HeadersOrReferences + if len(header.AdditionalProperties) != 0 { + headerOrEmpty = header + } + + var contentOrEmpty *openapi.MediaTypes + if len(content.AdditionalProperties) != 0 { + contentOrEmpty = content + } + + if headerOrEmpty == nil && contentOrEmpty == nil { + return nil + } + + return &openapi.NamedResponseOrReference{ + Name: name, + Value: &openapi.ResponseOrReference{ + Response: &openapi.Response{ + Description: description, + Headers: headerOrEmpty, + Content: contentOrEmpty, + }, + }, + } +} + func (g *OpenAPIGenerator) getDocumentAnnotationInWhichServiceOrStruct() (string, string) { var ret string for _, s := range g.ast.Services { - v := s.Annotations.Get(OpenapiDocument) + v := s.Annotations.Get(consts.OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "service", ret + return consts.DocumentOptionServiceType, ret } } for _, s := range g.ast.Structs { - v := s.Annotations.Get(OpenapiDocument) + v := s.Annotations.Get(consts.OpenapiDocument) if len(v) > 0 { ret = s.GetName() - return "struct", ret + return consts.DocumentOptionStructType, ret } } return "", ret } -func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrift_reflection.StructDescriptor) (string, *openapi.HeadersOrReferences, *openapi.MediaTypes) { +func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrift_reflection.StructDescriptor, statusCode string) (string, *openapi.HeadersOrReferences, *openapi.MediaTypes) { headers := &openapi.HeadersOrReferences{AdditionalProperties: []*openapi.NamedHeaderOrReference{}} for _, field := range desc.Fields { - if len(field.Annotations[ApiHeader]) < 1 { + if len(field.Annotations[consts.ApiHeader]) < 1 { continue } - if ext := field.Annotations[ApiHeader][0]; ext != "" { + if ext := field.Annotations[consts.ApiHeader][0]; ext != "" { headerName := ext header := &openapi.Header{ Description: g.filterCommentString(field.Comments), @@ -574,19 +627,19 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif } // Get api.body and api.raw_body option schema - bodySchema := g.getSchemaByOption(desc, ApiBody) - rawBodySchema := g.getSchemaByOption(desc, ApiRawBody) + bodySchema := g.getSchemaByOption(desc, consts.ApiBody) + rawBodySchema := g.getSchemaByOption(desc, consts.ApiRawBody) var additionalProperties []*openapi.NamedMediaType if len(bodySchema.Properties.AdditionalProperties) > 0 { refSchema := &openapi.NamedSchemaOrReference{ - Name: desc.GetName() + "Body", + Name: desc.GetName() + consts.ComponentSchemaSuffixBody, Value: &openapi.SchemaOrReference{Schema: bodySchema}, } - ref := "#/components/schemas/" + desc.GetName() + "Body" + ref := consts.ComponentSchemaPrefix + desc.GetName() + consts.ComponentSchemaSuffixBody g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "application/json", + Name: consts.ContentTypeJSON, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, @@ -597,13 +650,13 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif if len(rawBodySchema.Properties.AdditionalProperties) > 0 { refSchema := &openapi.NamedSchemaOrReference{ - Name: desc.GetName() + "RawBody", + Name: desc.GetName() + consts.ComponentSchemaSuffixRawBody, Value: &openapi.SchemaOrReference{Schema: rawBodySchema}, } - ref := "#/components/schemas/" + desc.GetName() + "RawBody" + ref := consts.ComponentSchemaPrefix + desc.GetName() + consts.ComponentSchemaSuffixRawBody g.addSchemaToDocument(d, refSchema) additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ - Name: "text/plain", + Name: consts.ContentTypeRawBody, Value: &openapi.MediaType{ Schema: &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, @@ -616,7 +669,7 @@ func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrif AdditionalProperties: additionalProperties, } - return "200", headers, content + return statusCode, headers, content } func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.StructDescriptor, option string) *openapi.Schema { @@ -626,7 +679,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct var allRequired []string var extSchema *openapi.Schema - err := utils.ParseStructOption(inputDesc, OpenapiSchema, &extSchema) + err := utils.ParseStructOption(inputDesc, consts.OpenapiSchema, &extSchema) if err != nil { logs.Errorf("Error parsing struct option: %s", err) } @@ -644,7 +697,7 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct extName = field.Annotations[option][0] } - if utils.Contains(allRequired, extName) { + if common.Contains(allRequired, extName) { required = append(required, extName) } @@ -658,11 +711,14 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct if fieldSchema.IsSetSchema() { fieldSchema.Schema.Description = description newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(field, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(field, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + err = common.MergeStructs(fieldSchema.Schema, newFieldSchema) + if err != nil { + logs.Errorf("Error merging field option: %s", err) + } } definitionProperties.AdditionalProperties = append( @@ -676,31 +732,25 @@ func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.Struct } schema := &openapi.Schema{ - Type: "object", + Type: consts.SchemaObjectType, Properties: definitionProperties, } if extSchema != nil { - utils.MergeStructs(schema, extSchema) + err := common.MergeStructs(schema, extSchema) + if err != nil { + logs.Errorf("Error merging struct option: %s", err) + } } schema.Required = required return schema } -func (g *OpenAPIGenerator) getStructLikeByName(name string) *parser.StructLike { - for _, s := range g.ast.GetStructLikes() { - if s.GetName() == name { - return s - } - } - return nil -} - // filterCommentString removes linter rules from comments. func (g *OpenAPIGenerator) filterCommentString(str string) string { var comments []string - matches := g.commentPattern.FindAllStringSubmatch(str, -1) + matches := regexp.MustCompile(consts.CommentPatternRegexp).FindAllStringSubmatch(str, -1) for _, match := range matches { if match[1] != "" { @@ -739,35 +789,41 @@ func (g *OpenAPIGenerator) filterCommentString(str string) string { return strings.Join(comments, "\n") } -func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, structs []*parser.StructLike) { - // Handle nested structs +func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, structs []*thrift_reflection.StructDescriptor) { for _, s := range structs { - var sls []*parser.StructLike + var sls []*thrift_reflection.StructDescriptor for _, f := range s.GetFields() { - if f.GetType().GetCategory().IsStruct() { - sls = append(sls, g.getStructLikeByName(f.GetType().GetName())) + fieldType := f.GetType() + if fieldType == nil { + logs.Errorf("Warning: field type is nil for field: %s\n", f.GetName()) + continue + } + if fieldType.IsStruct() { + structDesc, _ := fieldType.GetStructDescriptor() + sls = append(sls, structDesc) } } - g.addSchemasForStructsToDocument(d, sls) + if len(sls) > 0 { + g.addSchemasForStructsToDocument(d, sls) + } schemaName := s.GetName() + // Only generate this if we need it and haven't already generated it. - if !utils.Contains(g.requiredSchemas, schemaName) || - utils.Contains(g.generatedSchemas, schemaName) { + if !common.Contains(g.requiredSchemas, schemaName) || + common.Contains(g.generatedSchemas, schemaName) { continue } - structDesc := g.fileDesc.GetStructDescriptor(s.GetName()) - // Get the description from the comments. - messageDescription := g.filterCommentString(structDesc.Comments) + messageDescription := g.filterCommentString(s.Comments) // Build an array holding the fields of the message. definitionProperties := &openapi.Properties{ AdditionalProperties: make([]*openapi.NamedSchemaOrReference, 0), } - for _, field := range structDesc.Fields { + for _, field := range s.Fields { // Get the field description from the comments. description := g.filterCommentString(field.Comments) fieldSchema := g.schemaOrReferenceForField(field.Type) @@ -778,15 +834,18 @@ func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, s if fieldSchema.IsSetSchema() { fieldSchema.Schema.Description = description newFieldSchema := &openapi.Schema{} - err := utils.ParseFieldOption(field, OpenapiProperty, &newFieldSchema) + err := utils.ParseFieldOption(field, consts.OpenapiProperty, &newFieldSchema) if err != nil { logs.Errorf("Error parsing field option: %s", err) } - utils.MergeStructs(fieldSchema.Schema, newFieldSchema) + err = common.MergeStructs(fieldSchema.Schema, newFieldSchema) + if err != nil { + logs.Errorf("Error merging field option: %s", err) + } } extName := field.GetName() - options := []string{ApiHeader, ApiBody, ApiForm, ApiRawBody} + options := []string{consts.ApiHeader, consts.ApiBody, consts.ApiForm, consts.ApiRawBody} for _, option := range options { if field.Annotations[option] != nil && field.Annotations[option][0] != "" { extName = field.Annotations[option][0] @@ -803,18 +862,21 @@ func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, s } schema := &openapi.Schema{ - Type: "object", + Type: consts.SchemaObjectType, Description: messageDescription, Properties: definitionProperties, } var extSchema *openapi.Schema - err := utils.ParseStructOption(structDesc, OpenapiSchema, &extSchema) + err := utils.ParseStructOption(s, consts.OpenapiSchema, &extSchema) if err != nil { logs.Errorf("Error parsing struct option: %s", err) } if extSchema != nil { - utils.MergeStructs(schema, extSchema) + err = common.MergeStructs(schema, extSchema) + if err != nil { + logs.Errorf("Error merging struct option: %s", err) + } } // Add the schema to the components.schema list. @@ -829,7 +891,7 @@ func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, s // addSchemaToDocument adds the schema to the document if required func (g *OpenAPIGenerator) addSchemaToDocument(d *openapi.Document, schema *openapi.NamedSchemaOrReference) { - if utils.Contains(g.generatedSchemas, schema.Name) { + if common.Contains(g.generatedSchemas, schema.Name) { return } g.generatedSchemas = append(g.generatedSchemas, schema.Name) @@ -851,183 +913,148 @@ func (g *OpenAPIGenerator) addOperationToDocument(d *openapi.Document, op *opena } // Set the operation on the specified method. switch methodName { - case "GET": + case consts.HttpMethodGet: selectedPathItem.Value.Get = op - case "POST": + case consts.HttpMethodPost: selectedPathItem.Value.Post = op - case "PUT": + case consts.HttpMethodPut: selectedPathItem.Value.Put = op - case "DELETE": + case consts.HttpMethodDelete: selectedPathItem.Value.Delete = op - case "PATCH": + case consts.HttpMethodPatch: selectedPathItem.Value.Patch = op - case "OPTIONS": + case consts.HttpMethodOptions: selectedPathItem.Value.Options = op - case "HEAD": + case consts.HttpMethodHead: selectedPathItem.Value.Head = op } } func (g *OpenAPIGenerator) schemaReferenceForMessage(message *thrift_reflection.StructDescriptor) string { schemaName := message.GetName() - if !utils.Contains(g.requiredSchemas, schemaName) { + if !common.Contains(g.requiredSchemas, schemaName) { g.requiredSchemas = append(g.requiredSchemas, schemaName) + g.requiredTypeDesc = append(g.requiredTypeDesc, message) } - return "#/components/schemas/" + schemaName + return consts.ComponentSchemaPrefix + schemaName } func (g *OpenAPIGenerator) schemaOrReferenceForField(fieldType *thrift_reflection.TypeDescriptor) *openapi.SchemaOrReference { var kindSchema *openapi.SchemaOrReference - if fieldType.IsStruct() { + + switch { + case fieldType.IsStruct(): structDesc, err := fieldType.GetStructDescriptor() if err != nil { logs.Errorf("Error getting struct descriptor: %s", err) return nil } ref := g.schemaReferenceForMessage(structDesc) - kindSchema = &openapi.SchemaOrReference{ Reference: &openapi.Reference{Xref: ref}, } - } - - if fieldType.GetName() == "string" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - }, - } - } - - if fieldType.GetName() == "binary" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "string", - Format: "binary", - }, - } - } - if fieldType.GetName() == "bool" { + case fieldType.IsMap(): + valueSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ - Type: "boolean", + Type: consts.SchemaObjectType, + AdditionalProperties: &openapi.AdditionalPropertiesItem{ + SchemaOrReference: valueSchema, + }, }, } - } - if fieldType.GetName() == "byte" { + case fieldType.IsList(): + itemSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) kindSchema = &openapi.SchemaOrReference{ Schema: &openapi.Schema{ - Type: "string", - Format: "byte", + Type: "array", + Items: &openapi.ItemsItem{ + SchemaOrReference: []*openapi.SchemaOrReference{itemSchema}, + }, }, } - } - if fieldType.GetName() == "double" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "number", - Format: "double", - }, + case fieldType.IsTypedef(): + typedefDesc, err := fieldType.GetTypedefDescriptor() + if err != nil { + logs.Errorf("Error getting typedef descriptor: %s", err) + return nil } - } + kindSchema = g.schemaOrReferenceForField(typedefDesc.Type) - if fieldType.GetName() == "i8" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int8", - }, + case fieldType.IsEnum(): + enumDesc, err := fieldType.GetEnumDescriptor() + if err != nil { + logs.Errorf("Error getting enum descriptor: %s", err) + return nil } - } - - if fieldType.GetName() == "i16" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int16", - }, + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "enum" + kindSchema.Schema.Enum = make([]*openapi.Any, 0, len(enumDesc.GetValues())) + for _, v := range enumDesc.GetValues() { + kindSchema.Schema.Enum = append(kindSchema.Schema.Enum, &openapi.Any{Yaml: v.GetName()}) } - } - if fieldType.GetName() == "i32" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int32", - }, + case fieldType.IsUnion(): + unionDesc, err := fieldType.GetUnionDescriptor() + if err != nil { + logs.Errorf("Error getting union descriptor: %s", err) + return nil } - } - - if fieldType.GetName() == "i64" { - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "integer", - Format: "int64", - }, + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + kindSchema.Schema.OneOf = make([]*openapi.SchemaOrReference, 0, len(unionDesc.GetFields())) + for _, f := range unionDesc.GetFields() { + fieldSchema := g.schemaOrReferenceForField(f.Type) + kindSchema.Schema.OneOf = append(kindSchema.Schema.OneOf, fieldSchema) } - } - if fieldType.IsMap() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "object", - AdditionalProperties: &openapi.AdditionalPropertiesItem{ - SchemaOrReference: kindSchema, - }, - }, + case fieldType.IsException(): + logs.Errorf("Error: exception type not supported: %s for field", fieldType.GetName()) + + default: + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + switch fieldType.GetName() { + case "string": + kindSchema.Schema.Type = "string" + case "binary": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "binary" + case "bool": + kindSchema.Schema.Type = "boolean" + case "byte": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "byte" + case "double": + kindSchema.Schema.Type = "number" + kindSchema.Schema.Format = "double" + case "i8": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int8" + case "i16": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int16" + case "i32": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int32" + case "i64": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int64" } } - if fieldType.IsList() { - kindSchema = g.schemaOrReferenceForField(fieldType.GetValueType()) - kindSchema = &openapi.SchemaOrReference{ - Schema: &openapi.Schema{ - Type: "array", - Items: &openapi.ItemsItem{ - SchemaOrReference: []*openapi.SchemaOrReference{kindSchema}, - }, - }, - } - } return kindSchema } -const ( - ApiGet = "api.get" - ApiPost = "api.post" - ApiPut = "api.put" - ApiPatch = "api.patch" - ApiDelete = "api.delete" - ApiOptions = "api.options" - ApiHEAD = "api.head" - ApiAny = "api.any" - ApiQuery = "api.query" - ApiForm = "api.form" - ApiPath = "api.path" - ApiHeader = "api.header" - ApiCookie = "api.cookie" - ApiBody = "api.body" - ApiRawBody = "api.raw_body" - ApiBaseDomain = "api.base_domain" - ApiBaseURL = "api.baseurl" - OpenapiOperation = "openapi.operation" - OpenapiProperty = "openapi.property" - OpenapiSchema = "openapi.schema" - OpenapiParameter = "openapi.parameter" - OpenapiDocument = "openapi.document" -) - var HttpMethodAnnotations = map[string]string{ - ApiGet: "GET", - ApiPost: "POST", - ApiPut: "PUT", - ApiPatch: "PATCH", - ApiDelete: "DELETE", - ApiOptions: "OPTIONS", - ApiHEAD: "HEAD", - ApiAny: "ANY", + consts.ApiGet: "GET", + consts.ApiPost: "POST", + consts.ApiPut: "PUT", + consts.ApiPatch: "PATCH", + consts.ApiDelete: "DELETE", + consts.ApiOptions: "OPTIONS", + consts.ApiHEAD: "HEAD", + consts.ApiAny: "ANY", } diff --git a/thrift-gen-http-swagger/generator/server_gen.go b/thrift-gen-http-swagger/generator/server_gen.go new file mode 100644 index 0000000..e501bac --- /dev/null +++ b/thrift-gen-http-swagger/generator/server_gen.go @@ -0,0 +1,73 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package generator + +import ( + "bytes" + "errors" + "fmt" + "path/filepath" + "text/template" + + "github.com/cloudwego/thriftgo/parser" + "github.com/cloudwego/thriftgo/plugin" + "github.com/hertz-contrib/swagger-generate/common/consts" + "github.com/hertz-contrib/swagger-generate/common/tpl" + "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/args" +) + +type ServerGenerator struct { + OutputDir string +} + +func NewServerGenerator(ast *parser.Thrift, args *args.Arguments) (*ServerGenerator, error) { + defaultOutputDir := consts.DefaultOutputDir + + idlPath := ast.Filename + if idlPath == "" { + return nil, errors.New("failed to get Thrift file path") + } + + outputDir := args.OutputDir + if outputDir == "" { + outputDir = defaultOutputDir + } + + return &ServerGenerator{ + OutputDir: outputDir, + }, nil +} + +func (g *ServerGenerator) Generate() ([]*plugin.Generated, error) { + filePath := filepath.Join(g.OutputDir, consts.DefaultOutputSwaggerFile) + + tmpl, err := template.New("server").Delims("{{", "}}").Parse(consts.CodeGenerationCommentThriftHttp + "\n" + tpl.ServerTemplateHttp) + if err != nil { + return nil, fmt.Errorf("failed to parse template: %v", err) + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, g) + if err != nil { + return nil, fmt.Errorf("failed to execute template: %v", err) + } + + return []*plugin.Generated{{ + Content: buf.String(), + Name: &filePath, + }}, nil +} diff --git a/thrift-gen-http-swagger/go.mod b/thrift-gen-http-swagger/go.mod index 3fe5c1c..511d693 100644 --- a/thrift-gen-http-swagger/go.mod +++ b/thrift-gen-http-swagger/go.mod @@ -3,9 +3,13 @@ module github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger go 1.16 require ( - github.com/apache/thrift v0.13.0 + github.com/cloudwego/hertz v0.9.3 github.com/cloudwego/hertz/cmd/hz v0.9.0 github.com/cloudwego/thriftgo v0.3.15 - github.com/google/gnostic-models v0.6.8 - gopkg.in/yaml.v3 v3.0.1 + github.com/hertz-contrib/cors v0.1.0 + github.com/hertz-contrib/swagger v0.1.0 + github.com/hertz-contrib/swagger-generate v0.0.0 + github.com/swaggo/files v1.0.1 ) + +replace github.com/hertz-contrib/swagger-generate => ../../swagger-generate diff --git a/thrift-gen-http-swagger/go.sum b/thrift-gen-http-swagger/go.sum index 444c0ef..fee8c1c 100644 --- a/thrift-gen-http-swagger/go.sum +++ b/thrift-gen-http-swagger/go.sum @@ -1,27 +1,85 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs= +github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94zf2JZ3X4= +github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4= +github.com/bytedance/mockey v1.2.12/go.mod h1:3ZA4MQasmqC87Tw0w7Ygdy7eHIc2xgpZ8Pona5rsYIk= +github.com/bytedance/sonic v1.3.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls= +github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= +github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/hertz v0.0.1/go.mod h1:prTyExvsH/UmDkvfU3dp3EHsZFQISfT8R7BirvpTKdo= +github.com/cloudwego/hertz v0.6.2/go.mod h1:2em2hGREvCBawsTQcQxyWBGVlCeo+N1pp2q0HkkbwR0= +github.com/cloudwego/hertz v0.9.3 h1:uajvLn6LjEPjUqN/ewUZtWoRQWa2es2XTELdqDlOYMw= +github.com/cloudwego/hertz v0.9.3/go.mod h1:gGVUfJU/BOkJv/ZTzrw7FS7uy7171JeYIZvAyV3wS3o= github.com/cloudwego/hertz/cmd/hz v0.9.0 h1:qkB5KpYklnsS5PbGjwtb/UXDtKPZzdXehwy4wGeWXA0= github.com/cloudwego/hertz/cmd/hz v0.9.0/go.mod h1:6SroAwvZkyL54CiPANDkTR3YoX2MY4ZOW1+gtmWhRJE= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/netpoll v0.2.4/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.6.2 h1:+KdILv5ATJU+222wNNXpHapYaBeRvvL8qhJyhcxRxrQ= +github.com/cloudwego/netpoll v0.6.2/go.mod h1:kaqvfZ70qd4T2WtIIpCOi5Cxyob8viEpzLhCrTrz3HM= github.com/cloudwego/thriftgo v0.1.7/go.mod h1:LzeafuLSiHA9JTiWC8TIMIq64iadeObgRUhmVG1OC/w= github.com/cloudwego/thriftgo v0.3.15 h1:yB/DDGjeSjliyidMVBjKhGl9RgE4M8iVIz5dKpAIyUs= github.com/cloudwego/thriftgo v0.3.15/go.mod h1:R4a+4aVDI0V9YCTfpNgmvbkq/9ThKgF7Om8Z0I36698= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -34,8 +92,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -45,9 +104,21 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/hertz-contrib/cors v0.1.0 h1:PQ5mATygSMzTlYtfyMyHjobYoJeHKe2Qt3tcAOgbI6E= +github.com/hertz-contrib/cors v0.1.0/go.mod h1:VPReoq+Rvu/lZOfpp5CcX3x4mpZUc3EpSXBcVDcbvOc= +github.com/hertz-contrib/swagger v0.1.0 h1:FlnMPRHuvAt/3pt3KCQRZ6RH1g/agma9SU70Op2Pb58= +github.com/hertz-contrib/swagger v0.1.0/go.mod h1:Bt5i+Nyo7bGmYbuEfMArx7raf1oK+nWVgYbEvhpICKE= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= @@ -55,34 +126,103 @@ github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+ github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= +github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/urfave/cli/v2 v2.23.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -91,45 +231,80 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -155,18 +330,29 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/thrift-gen-http-swagger/main.go b/thrift-gen-http-swagger/main.go index 079d0ee..232d7f1 100644 --- a/thrift-gen-http-swagger/main.go +++ b/thrift-gen-http-swagger/main.go @@ -20,6 +20,7 @@ import ( "flag" "os" + "github.com/cloudwego/hertz/cmd/hz/util/logs" "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/plugins" ) @@ -30,7 +31,7 @@ func main() { f.BoolVar(&queryVersion, "version", false, "Show the version of thrift-gen-http-swagger") if err := f.Parse(os.Args[1:]); err != nil { - println(err) + logs.Error("Failed to parse flags: %v", err) os.Exit(2) } diff --git a/thrift-gen-http-swagger/plugins/plugins.go b/thrift-gen-http-swagger/plugins/plugins.go index d3aa971..d65c171 100644 --- a/thrift-gen-http-swagger/plugins/plugins.go +++ b/thrift-gen-http-swagger/plugins/plugins.go @@ -17,11 +17,11 @@ package plugins import ( - "fmt" + "errors" "io" - "log" "os" + "github.com/cloudwego/hertz/cmd/hz/util/logs" "github.com/cloudwego/thriftgo/plugin" "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/args" "github.com/hertz-contrib/swagger-generate/thrift-gen-http-swagger/generator" @@ -30,18 +30,18 @@ import ( func Run() int { data, err := io.ReadAll(os.Stdin) if err != nil { - println("Failed to get input:", err.Error()) + logs.Errorf("Failed to get input: %v", err.Error()) os.Exit(1) } req, err := plugin.UnmarshalRequest(data) if err != nil { - println("Failed to unmarshal request:", err.Error()) + logs.Errorf("Failed to unmarshal request: %v", err.Error()) os.Exit(1) } if err := handleRequest(req); err != nil { - println("Failed to handle request:", err.Error()) + logs.Errorf("Failed to handle request: %v", err.Error()) os.Exit(1) } @@ -50,20 +50,31 @@ func Run() int { } func handleRequest(req *plugin.Request) (err error) { + if req == nil { + return errors.New("request is nil") + } + args := new(args.Arguments) if err := args.Unpack(req.PluginParameters); err != nil { - log.Printf("[Error]: unpack args failed: %s", err.Error()) return err } - if req == nil { - fmt.Fprintf(os.Stderr, "unexpected nil request") - } ast := req.GetAST() - g := generator.NewOpenAPIGenerator(ast) - contents := g.BuildDocument(args) + + og := generator.NewOpenAPIGenerator(ast) + openapiContent := og.BuildDocument(args) + + sg, err := generator.NewServerGenerator(ast, args) + if err != nil { + return err + } + serverContent, err := sg.Generate() + if err != nil { + return err + } + res := &plugin.Response{ - Contents: contents, + Contents: append(openapiContent, serverContent...), } if err := handleResponse(res); err != nil { return err diff --git a/thrift-gen-http-swagger/utils/utils.go b/thrift-gen-http-swagger/utils/utils.go index e8080ca..26950c7 100644 --- a/thrift-gen-http-swagger/utils/utils.go +++ b/thrift-gen-http-swagger/utils/utils.go @@ -19,29 +19,16 @@ package utils import ( "encoding/json" "errors" - "fmt" - "reflect" - "strconv" - "strings" "github.com/cloudwego/thriftgo/extension/thrift_option" - "github.com/cloudwego/thriftgo/parser" "github.com/cloudwego/thriftgo/thrift_reflection" ) -// Contains returns true if an array Contains a specified string. -func Contains(s []string, e string) bool { - for _, a := range s { - if a == e { - return true - } - } - return false -} - func ParseStructOption(descriptor *thrift_reflection.StructDescriptor, optionName string, obj interface{}) error { opt, err := thrift_option.ParseStructOption(descriptor, optionName) - if errors.Is(err, thrift_option.ErrKeyNotMatch) { + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { return nil } if err != nil { @@ -61,7 +48,9 @@ func ParseStructOption(descriptor *thrift_reflection.StructDescriptor, optionNam func ParseServiceOption(descriptor *thrift_reflection.ServiceDescriptor, optionName string, obj interface{}) error { opt, err := thrift_option.ParseServiceOption(descriptor, optionName) - if errors.Is(err, thrift_option.ErrKeyNotMatch) { + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { return nil } if err != nil { @@ -81,7 +70,9 @@ func ParseServiceOption(descriptor *thrift_reflection.ServiceDescriptor, optionN func ParseMethodOption(descriptor *thrift_reflection.MethodDescriptor, optionName string, obj interface{}) error { opt, err := thrift_option.ParseMethodOption(descriptor, optionName) - if errors.Is(err, thrift_option.ErrKeyNotMatch) { + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { return nil } if err != nil { @@ -101,7 +92,9 @@ func ParseMethodOption(descriptor *thrift_reflection.MethodDescriptor, optionNam func ParseFieldOption(descriptor *thrift_reflection.FieldDescriptor, optionName string, obj interface{}) error { opt, err := thrift_option.ParseFieldOption(descriptor, optionName) - if errors.Is(err, thrift_option.ErrKeyNotMatch) { + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { return nil } if err != nil { @@ -119,159 +112,15 @@ func ParseFieldOption(descriptor *thrift_reflection.FieldDescriptor, optionName return err } -func UnpackArgs(args []string, c interface{}) error { - m, err := MapForm(args) - if err != nil { - return fmt.Errorf("unmarshal args failed, err: %v", err.Error()) - } - - t := reflect.TypeOf(c).Elem() - v := reflect.ValueOf(c).Elem() - if t.Kind() != reflect.Struct { - return errors.New("passed c must be struct or pointer of struct") - } - - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - x := v.Field(i) - n := f.Name - values, ok := m[n] - if !ok || len(values) == 0 || values[0] == "" { - continue - } - switch x.Kind() { - case reflect.Bool: - if len(values) != 1 { - return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) - } - x.SetBool(values[0] == "true") - case reflect.String: - if len(values) != 1 { - return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) - } - x.SetString(values[0]) - case reflect.Slice: - if len(values) != 1 { - return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) - } - ss := strings.Split(values[0], ";") - if x.Type().Elem().Kind() == reflect.Int { - n := reflect.MakeSlice(x.Type(), len(ss), len(ss)) - for i, s := range ss { - val, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return err - } - n.Index(i).SetInt(val) - } - x.Set(n) - } else { - for _, s := range ss { - val := reflect.Append(x, reflect.ValueOf(s)) - x.Set(val) - } - } - case reflect.Map: - if len(values) != 1 { - return fmt.Errorf("field %s can't be assigned multi values: %v", n, values) - } - ss := strings.Split(values[0], ";") - out := make(map[string]string, len(ss)) - for _, s := range ss { - sk := strings.SplitN(s, "=", 2) - if len(sk) != 2 { - return fmt.Errorf("map filed %v invalid key-value pair '%v'", n, s) - } - out[sk[0]] = sk[1] - } - x.Set(reflect.ValueOf(out)) - default: - return fmt.Errorf("field %s has unsupported type %+v", n, f.Type) - } - } - return nil -} - -func MapForm(input []string) (map[string][]string, error) { - out := make(map[string][]string, len(input)) - - for _, str := range input { - parts := strings.SplitN(str, "=", 2) - if len(parts) != 2 { - return nil, fmt.Errorf("invalid argument: '%s'", str) - } - key, val := parts[0], parts[1] - out[key] = append(out[key], val) - } - - return out, nil -} - -// MergeStructs merges non-zero fields from src into dst. -func MergeStructs(dst, src interface{}) error { - dstVal := reflect.ValueOf(dst) - srcVal := reflect.ValueOf(src) - - // Ensure both dst and src are pointers to structs. - if dstVal.Kind() != reflect.Ptr || srcVal.Kind() != reflect.Ptr { - return errors.New("both dst and src must be pointers") - } - if dstVal.Elem().Kind() != reflect.Struct || srcVal.Elem().Kind() != reflect.Struct { - return errors.New("both dst and src must be pointers to structs") - } - - dstVal = dstVal.Elem() - srcVal = srcVal.Elem() - - for i := 0; i < dstVal.NumField(); i++ { - field := dstVal.Field(i) - srcField := srcVal.Field(i) - - if !srcField.IsZero() { - field.Set(srcField) - } - } - - return nil -} - -func GetAnnotation(input parser.Annotations, target string) []string { - if len(input) == 0 { - return nil - } - for _, anno := range input { - if strings.ToLower(anno.Key) == target { - return anno.Values - } - } - - return []string{} -} - -func GetAnnotations(input parser.Annotations, targets map[string]string) map[string][]string { +func GetAnnotations(input map[string][]string, targets map[string]string) map[string][]string { if len(input) == 0 || len(targets) == 0 { return nil } out := map[string][]string{} for k, t := range targets { - var ret *parser.Annotation - for _, anno := range input { - if strings.ToLower(anno.Key) == k { - ret = anno - break - } - } - if ret == nil { - continue + if v, ok := input[k]; ok { + out[t] = v } - out[t] = ret.Values } return out } - -func AppendUnique(s []string, e string) []string { - if !Contains(s, e) { - return append(s, e) - } - return s -} diff --git a/thrift-gen-rpc-swagger/README.md b/thrift-gen-rpc-swagger/README.md new file mode 100644 index 0000000..ea2054f --- /dev/null +++ b/thrift-gen-rpc-swagger/README.md @@ -0,0 +1,76 @@ +# thrift-gen-rpc-swagger + +English | [中文](README_CN.md) + +This is a plugin for generating RPC Swagger documentation and providing Swagger-UI access and debugging for [cloudwego/cwgo](https://github.com/cloudwego/cwgo) & [kitex](https://github.com/cloudwego/kitex). + +## Installation + +```sh +# Install from the official repository + +git clone https://github.com/hertz-contrib/swagger-generate +cd swagger-generate +go install + +# Direct installation +go install github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger@latest + +# Verify installation +thrift-gen-rpc-swagger --version +``` + +## Usage + +### Generating Swagger Documentation + +```sh +thriftgo -g go -p rpc-swagger hello.thrift +``` + +### Add the option during Kitex Server initialization + +```sh +svr := api.NewServer(new(HelloImpl), server.WithTransHandlerFactory(&swagger.MixTransHandlerFactory{})) +``` + +### Access Swagger UI (Kitex service needs to be running for debugging) + +```sh +http://127.0.0.1:8888/swagger/index.html +``` + +## Usage Instructions + +### Debugging Notes +1. The plugin generates Swagger documentation and also sets up an HTTP (Hertz) service to provide access to the Swagger documentation and debugging. +2. The HTTP service defaults to the same port as the RPC service, implemented via protocol sniffing. +3. Accessing the Swagger documentation and debugging the RPC service requires adding `"server.WithTransHandlerFactory(&swagger.MixTransHandlerFactory{})"` to the Kitex Server initialization. + +### Generation Notes +1. All RPC methods are converted into HTTP POST methods, with request parameters corresponding to the Request body in `application/json` format, and the same for the return value. +2. Swagger documentation can be supplemented with annotations such as `openapi.operation`, `openapi.property`, `openapi.schema`, `api.base_domain`, and `api.baseurl`. +3. To use annotations like `openapi.operation`, `openapi.property`, `openapi.schema`, and `openapi.document`, you need to import `openapi.thrift`. +4. Custom HTTP services are supported, and custom parts will not be overwritten during updates. + +### Metadata Transmission +1. Metadata transmission is supported. By default, the plugin generates a `ttheader` query parameter for each method to transmit metadata, which should be in JSON format, e.g., `{"p_k":"p_v","k":"v"}`. +2. Single-hop metadata transmission format is `"key":"value"`. +3. Continuous metadata transmission format is `"p_key":"value"`, with a prefix `p_`. +4. Reverse metadata transmission is supported; if enabled, metadata can be viewed in the return value, appended to the response in `"key":"value"` format. +5. For more information on using metadata, refer to [Metainfo](https://www.cloudwego.io/zh/docs/kitex/tutorials/advanced-feature/metainfo/). + +## Supported Annotations + +| Annotation | Component | Description | +|---------------------|-----------|------------------------------------------------------------------------------------------| +| `openapi.operation` | Method | Supplements the `operation` of `pathItem` | +| `openapi.property` | Field | Supplements the `property` of `schema` | +| `openapi.schema` | Struct | Supplements the `schema` for `requestBody` and `response` | +| `openapi.document` | Service | Supplements Swagger documentation; add this annotation to any service | +| `api.base_domain` | Service | Corresponds to `server`'s `url`, specifies the URL for the service | +| `api.baseurl` | Method | Corresponds to `pathItem`'s `server`'s `url`, specifies the URL for an individual method | + +## More Information + +For more usage instructions, refer to [Example](example/hello.thrift). \ No newline at end of file diff --git a/thrift-gen-rpc-swagger/README_CN.md b/thrift-gen-rpc-swagger/README_CN.md new file mode 100644 index 0000000..4f7ad01 --- /dev/null +++ b/thrift-gen-rpc-swagger/README_CN.md @@ -0,0 +1,85 @@ +# thrift-gen-rpc-swagger + +[English](README.md) | 中文 + +适用于 [cloudwego/cwgo](https://github.com/cloudwego/cwgo) & [kitex](https://github.com/cloudwego/kitex) 的 rpc swagger 文档生成及 swagger-ui 访问调试插件。 + +## 安装 + +```sh + +# 官方仓库安装 + +git clone https://github.com/hertz-contrib/swagger-generate +cd thrift-gen-rpc-swagger +go install + +# 直接安装 +go install github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger@latest + +# 验证安装 +thrift-gen-rpc-swagger --version +``` + +## 使用 + +### 生成 swagger 文档 + +```sh + +thriftgo -g go -p rpc-swagger hello.thrift + +``` +### 在 Kitex Server 初始化中添加 option + +```sh + +svr := api.NewServer(new(HelloImpl), server.WithTransHandlerFactory(&swagger.MixTransHandlerFactory{})) + +``` + +### 访问 swagger-ui (调试需启动 Kitex 服务) + +```sh + +http://127.0.0.1:8888/swagger/index.html +``` + +## 使用说明 + +### 调试说明 +1. 插件会生成 swagger 文档,并且会生成一个 http (Hertz) 服务, 用于提供 swagger 文档的访问及调试。 +2. http 服务默认和 rpc 服务在一个端口, 通过嗅探协议实现。 +3. swagger 文档的访问及 rpc 服务的调试需在 Kitex Server 初始化中加入 "server.WithTransHandlerFactory(&swagger.MixTransHandlerFactory{})"。 + +### 生成说明 +1. 所有的 rpc 方法会转换成 http 的 post 方法,请求参数对应 Request body, content 类型为 application/json 格式,返回值同上。 +2. 可通过注解来补充 swagger 文档的信息,如 `openapi.operation`, `openapi.property`, `openapi.schema`, `api.base_domain`, `api.baseurl`。 +3. 如需使用`openapi.operation`, `openapi.property`, `openapi.schema`, `openpai.document` 注解,需引用 openapi.thrift。 +4. 支持自定义 http 服务,自定义部分更新时不会被覆盖。 + +### 元信息传递 +1. 支持元信息传递, 插件默认为每个方法生成一个`ttheader`的查询参数, 用于传递元信息, 格式需满足 json 格式, 如{"p_k":"p_v","k":"v"}。 +2. 单跳透传元信息, 格式为 "key":"value"。 +3. 持续透传元信息, 格式为 "p_key":"value", 需添加前缀`p_`。 +4. 支持反向透传元信息, 若设置则可在返回值中查看到元信息, 返回通过"key":"value"的格式附加在响应中。 +5. 更多使用元信息可参考 [Metainfo](https://www.cloudwego.io/zh/docs/kitex/tutorials/advanced-feature/metainfo/)。 + +## 支持的注解 + +| 注解 | 使用组件 | 说明 | +|---------------------|---------|-------------------------------------------------------| +| `openapi.operation` | Method | 用于补充 `pathItem` 的 `operation` | +| `openapi.property` | Field | 用于补充 `schema` 的 `property` | +| `openapi.schema` | Struct | 用于补充 `requestBody` 和 `response` 的 `schema` | +| `openapi.document` | Service | 用于补充 swagger 文档,任意 service 中添加该注解即可 | +| `api.base_domain` | Service | 对应 `server` 的 `url`, 用于指定 service 服务的 url | +| `api.baseurl` | Method | 对应 `pathItem` 的 `server` 的 `url`, 用于指定单个 method 的 url | + +## 更多信息 + +更多的使用方法请参考 [示例](example/hello.thrift) + + + + diff --git a/thrift-gen-rpc-swagger/args/argument.go b/thrift-gen-rpc-swagger/args/argument.go new file mode 100644 index 0000000..e4c64a6 --- /dev/null +++ b/thrift-gen-rpc-swagger/args/argument.go @@ -0,0 +1,37 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package args + +import ( + "fmt" + + "github.com/hertz-contrib/swagger-generate/common/utils" +) + +type Arguments struct { + OutputDir string + HertzAddr string + KitexAddr string +} + +func (a *Arguments) Unpack(args []string) error { + err := utils.UnpackArgs(args, a) + if err != nil { + return fmt.Errorf("unpack argument failed: %s", err) + } + return nil +} diff --git a/thrift-gen-rpc-swagger/example/hello.thrift b/thrift-gen-rpc-swagger/example/hello.thrift new file mode 100644 index 0000000..ba7eb2a --- /dev/null +++ b/thrift-gen-rpc-swagger/example/hello.thrift @@ -0,0 +1,77 @@ +namespace go example + +include "openapi.thrift" + +// QueryReq +struct QueryReq { + 1: string QueryValue ( + openapi.property = '{ + title: "Name", + description: "Name", + type: "string", + min_length: 1, + max_length: 50 + }' + ) + 2: list Items () +} + +// PathReq +struct PathReq { + //field: path描述 + 1: string PathValue () +} + +//BodyReq +struct BodyReq { + //field: body描述 + 1: string BodyValue () + + //field: query描述 + 2: string QueryValue () +} + +// HelloResp +struct HelloResp { + 1: string RespBody ( + openapi.property = '{ + title: "response content", + description: "response content", + type: "string", + min_length: 1, + max_length: 80 + }' + ) + 2: string token ( + openapi.property = '{ + title: "token", + description: "token", + type: "string" + }' + ) +}( + openapi.schema = '{ + title: "Hello - response", + description: "Hello - response", + required: [ + "RespBody" + ] + }' +) + +// HelloService1描述 +service HelloService1 { + HelloResp QueryMethod(1: QueryReq req) () + + HelloResp PathMethod(1: PathReq req) () + + HelloResp BodyMethod(1: BodyReq req) () +}( + api.base_domain = "127.0.0.1:8888", + openapi.document = '{ + info: { + title: "example swagger doc", + version: "Version from annotation" + } + }' +) \ No newline at end of file diff --git a/thrift-gen-http-swagger/thrift/openapi.thrift b/thrift-gen-rpc-swagger/example/openapi.thrift similarity index 100% rename from thrift-gen-http-swagger/thrift/openapi.thrift rename to thrift-gen-rpc-swagger/example/openapi.thrift diff --git a/thrift-gen-rpc-swagger/example/swagger/openapi.yaml b/thrift-gen-rpc-swagger/example/swagger/openapi.yaml new file mode 100644 index 0000000..27b9db5 --- /dev/null +++ b/thrift-gen-rpc-swagger/example/swagger/openapi.yaml @@ -0,0 +1,127 @@ +# Generated with thrift-gen-rpc-swagger +# https://github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger + +openapi: 3.0.3 +info: + title: example swagger doc + description: HelloService1描述 + version: Version from annotation +servers: + - url: http://127.0.0.1:8888 +paths: + /BodyMethod: + post: + tags: + - HelloService1 + operationId: HelloService1_BodyMethod + parameters: + - name: ttheader + in: query + description: metainfo for request + schema: + type: object + requestBody: + description: BodyReq + content: + application/json: + schema: + type: object + properties: + BodyValue: + type: string + description: 'field: body描述' + QueryValue: + type: string + description: 'field: query描述' + responses: + "200": + description: HelloResp + content: + application/json: + schema: + $ref: '#/components/schemas/HelloResp' + /PathMethod: + post: + tags: + - HelloService1 + operationId: HelloService1_PathMethod + parameters: + - name: ttheader + in: query + description: metainfo for request + schema: + type: object + requestBody: + description: PathReq + content: + application/json: + schema: + type: object + properties: + PathValue: + type: string + description: 'field: path描述' + responses: + "200": + description: HelloResp + content: + application/json: + schema: + $ref: '#/components/schemas/HelloResp' + /QueryMethod: + post: + tags: + - HelloService1 + operationId: HelloService1_QueryMethod + parameters: + - name: ttheader + in: query + description: metainfo for request + schema: + type: object + requestBody: + description: QueryReq + content: + application/json: + schema: + type: object + properties: + QueryValue: + title: Name + maxLength: 50 + minLength: 1 + type: string + description: Name + Items: + type: array + items: + type: string + responses: + "200": + description: HelloResp + content: + application/json: + schema: + $ref: '#/components/schemas/HelloResp' +components: + schemas: + HelloResp: + title: Hello - response + required: + - RespBody + type: object + properties: + RespBody: + title: response content + maxLength: 80 + minLength: 1 + type: string + description: response content + token: + title: token + type: string + description: token + description: Hello - response +tags: + - name: HelloService1 + description: HelloService1描述 diff --git a/thrift-gen-rpc-swagger/example/swagger/swagger.go b/thrift-gen-rpc-swagger/example/swagger/swagger.go new file mode 100644 index 0000000..92a7ecb --- /dev/null +++ b/thrift-gen-rpc-swagger/example/swagger/swagger.go @@ -0,0 +1,206 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Code generated by thrift-gen-rpc-swagger. +package main + +import ( + "context" + _ "embed" + "encoding/json" + "errors" + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/bytedance/gopkg/cloud/metainfo" + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/cloudwego/kitex/client" + "github.com/cloudwego/kitex/client/genericclient" + "github.com/cloudwego/kitex/pkg/generic" + "github.com/cloudwego/kitex/pkg/transmeta" + "github.com/cloudwego/kitex/transport" + "github.com/hertz-contrib/cors" + "github.com/hertz-contrib/swagger" + swaggerFiles "github.com/swaggo/files" +) + +//go:embed openapi.yaml +var openapiYAML []byte + +func main() { + h := server.Default(server.WithHostPorts("127.0.0.1:8080")) + + h.Use(cors.Default()) + + cli := initializeGenericClient() + setupSwaggerRoutes(h) + setupProxyRoutes(h, cli) + + hlog.Info("Swagger UI is available at: http://127.0.0.1:8080/swagger/index.html") + + h.Spin() +} + +func findThriftFile(fileName string) (string, error) { + workingDir, err := os.Getwd() + if err != nil { + return "", err + } + + foundPath := "" + err = filepath.Walk(workingDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if !info.IsDir() && info.Name() == fileName { + foundPath = path + return filepath.SkipDir + } + return nil + }) + + if err == nil && foundPath != "" { + return foundPath, nil + } + + parentDir := filepath.Dir(workingDir) + for parentDir != "/" && parentDir != "." && parentDir != workingDir { + filePath := filepath.Join(parentDir, fileName) + if _, err := os.Stat(filePath); err == nil { + return filePath, nil + } + workingDir = parentDir + parentDir = filepath.Dir(parentDir) + } + + return "", errors.New("thrift file not found: " + fileName) +} + +func initializeGenericClient() genericclient.Client { + thriftFile, err := findThriftFile("hello.thrift") + if err != nil { + hlog.Fatal("Failed to locate Thrift file:", err) + } + + p, err := generic.NewThriftFileProvider(thriftFile) + if err != nil { + hlog.Fatal("Failed to create ThriftFileProvider:", err) + } + + g, err := generic.JSONThriftGeneric(p) + if err != nil { + hlog.Fatal("Failed to create HTTPThriftGeneric:", err) + } + var opts []client.Option + opts = append(opts, client.WithTransportProtocol(transport.TTHeader)) + opts = append(opts, client.WithMetaHandler(transmeta.ClientTTHeaderHandler)) + opts = append(opts, client.WithHostPorts("127.0.0.1:8888")) + cli, err := genericclient.NewClient("swagger", g, opts...) + if err != nil { + hlog.Fatal("Failed to create generic client:", err) + } + + return cli +} + +func setupSwaggerRoutes(h *server.Hertz) { + h.GET("swagger/*any", swagger.WrapHandler(swaggerFiles.Handler, swagger.URL("/openapi.yaml"))) + + h.GET("/openapi.yaml", func(c context.Context, ctx *app.RequestContext) { + ctx.Header("Content-Type", "application/x-yaml") + ctx.Write(openapiYAML) + }) +} + +func setupProxyRoutes(h *server.Hertz, cli genericclient.Client) { + h.Any("/*ServiceMethod", func(c context.Context, ctx *app.RequestContext) { + serviceMethod := ctx.Param("ServiceMethod") + if serviceMethod == "" { + handleError(ctx, "ServiceMethod not provided", http.StatusBadRequest) + return + } + + bodyBytes := ctx.Request.Body() + + queryMap := formatQueryParams(ctx) + + for k, v := range queryMap { + if strings.HasPrefix(k, "p_") { + c = metainfo.WithPersistentValue(c, k, v) + } else { + c = metainfo.WithValue(c, k, v) + } + } + + c = metainfo.WithBackwardValues(c) + + jReq := string(bodyBytes) + + jRsp, err := cli.GenericCall(c, serviceMethod, jReq) + if err != nil { + hlog.Errorf("GenericCall error: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": err.Error(), + }) + return + } + + result := make(map[string]interface{}) + if err := json.Unmarshal([]byte(jRsp.(string)), &result); err != nil { + hlog.Errorf("Failed to unmarshal response body: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": "Failed to unmarshal response body", + }) + return + } + + m := metainfo.RecvAllBackwardValues(c) + + for key, value := range m { + result[key] = value + } + + respBody, err := json.Marshal(result) + if err != nil { + hlog.Errorf("Failed to marshal response body: %v", err) + ctx.JSON(500, map[string]interface{}{ + "error": "Failed to marshal response body", + }) + return + } + + ctx.Data(http.StatusOK, "application/json", respBody) + }) +} + +func formatQueryParams(ctx *app.RequestContext) map[string]string { + QueryParams := make(map[string]string) + ctx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) { + QueryParams[string(key)] = string(value) + }) + return QueryParams +} + +func handleError(ctx *app.RequestContext, errMsg string, statusCode int) { + hlog.Errorf("Error: %s", errMsg) + ctx.JSON(statusCode, map[string]interface{}{ + "error": errMsg, + }) +} diff --git a/thrift-gen-rpc-swagger/generator/openapi_gen.go b/thrift-gen-rpc-swagger/generator/openapi_gen.go new file mode 100644 index 0000000..1d8e1cd --- /dev/null +++ b/thrift-gen-rpc-swagger/generator/openapi_gen.go @@ -0,0 +1,871 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2020 Google LLC. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file may have been modified by CloudWeGo authors. All CloudWeGo + * Modifications are Copyright 2024 CloudWeGo Authors. + */ + +package generator + +import ( + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/cloudwego/hertz/cmd/hz/util/logs" + "github.com/cloudwego/thriftgo/parser" + "github.com/cloudwego/thriftgo/plugin" + "github.com/cloudwego/thriftgo/thrift_reflection" + "github.com/hertz-contrib/swagger-generate/common/consts" + common "github.com/hertz-contrib/swagger-generate/common/utils" + openapi "github.com/hertz-contrib/swagger-generate/idl/thrift" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/args" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/utils" +) + +type OpenAPIGenerator struct { + fileDesc *thrift_reflection.FileDescriptor + ast *parser.Thrift + generatedSchemas []string + requiredSchemas []string + requiredTypeDesc []*thrift_reflection.StructDescriptor +} + +// NewOpenAPIGenerator creates a new generator for a protoc plugin invocation. +func NewOpenAPIGenerator(ast *parser.Thrift) *OpenAPIGenerator { + _, fileDesc := thrift_reflection.RegisterAST(ast) + return &OpenAPIGenerator{ + fileDesc: fileDesc, + ast: ast, + generatedSchemas: make([]string, 0), + } +} + +func (g *OpenAPIGenerator) BuildDocument(arguments *args.Arguments) []*plugin.Generated { + d := &openapi.Document{} + + version := consts.OpenAPIVersion + d.Openapi = version + d.Info = &openapi.Info{ + Title: consts.DefaultInfoTitle + consts.PluginNameThriftRpcSwagger, + Description: consts.DefaultInfoDesc, + Version: consts.DefaultInfoVersion, + } + d.Paths = &openapi.Paths{} + d.Components = &openapi.Components{ + Schemas: &openapi.SchemasOrReferences{ + AdditionalProperties: []*openapi.NamedSchemaOrReference{}, + }, + } + + var extDocument *openapi.Document + err := g.getDocumentOption(&extDocument) + if err != nil { + logs.Errorf("Error getting document option: %s", err) + return nil + } + if extDocument != nil { + err := common.MergeStructs(d, extDocument) + if err != nil { + logs.Errorf("Error merging document option: %s", err) + return nil + } + } + + g.addPathsToDocument(d, g.fileDesc.GetServices()) + + for len(g.requiredSchemas) > 0 { + count := len(g.requiredSchemas) + g.addSchemasForStructsToDocument(d, g.requiredTypeDesc) + g.requiredSchemas = g.requiredSchemas[count:len(g.requiredSchemas)] + } + + // If there is only 1 service, then use it's title for the + // document, if the document is missing it. + if len(d.Tags) == 1 { + if d.Info.Title == "" && d.Tags[0].Name != "" { + d.Info.Title = d.Tags[0].Name + " API" + } + if d.Info.Description == "" { + d.Info.Description = d.Tags[0].Description + } + } + + var allServers []string + + // If paths methods has servers, but they're all the same, then move servers to path level + for _, path := range d.Paths.Path { + var servers []string + // Only 1 server will ever be set, per method, by the generator + if path.Value.Post != nil && len(path.Value.Post.Servers) == 1 { + servers = common.AppendUnique(servers, path.Value.Post.Servers[0].URL) + allServers = common.AppendUnique(allServers, path.Value.Post.Servers[0].URL) + } + + if len(servers) == 1 { + path.Value.Servers = []*openapi.Server{{URL: servers[0]}} + + if path.Value.Post != nil { + path.Value.Post.Servers = nil + } + } + } + + // Set all servers on API level + if len(allServers) > 0 { + d.Servers = []*openapi.Server{} + for _, server := range allServers { + d.Servers = append(d.Servers, &openapi.Server{URL: server}) + } + } + + // If there is only 1 server, we can safely remove all path level servers + if len(allServers) == 1 { + for _, path := range d.Paths.Path { + path.Value.Servers = nil + } + } + + // If there are no servers, add a default one + if len(allServers) == 0 { + d.Servers = []*openapi.Server{ + {URL: consts.DefaultServerURL}, + } + } + + { + pairs := d.Tags + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].Name < pairs[j].Name + }) + d.Tags = pairs + } + + { + pairs := d.Paths.Path + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].Name < pairs[j].Name + }) + d.Paths.Path = pairs + } + + { + pairs := d.Components.Schemas.AdditionalProperties + sort.Slice(pairs, func(i, j int) bool { + return pairs[i].Name < pairs[j].Name + }) + d.Components.Schemas.AdditionalProperties = pairs + } + + bytes, err := d.YAMLValue("Generated with " + consts.PluginNameThriftRpcSwagger + "\n" + consts.InfoURL + consts.PluginNameThriftRpcSwagger) + if err != nil { + logs.Errorf("Error converting to yaml: %s", err) + return nil + } + outputDir := arguments.OutputDir + if outputDir == "" { + outputDir = consts.DefaultOutputDir + } + filePath := filepath.Join(outputDir, consts.DefaultOutputYamlFile) + var ret []*plugin.Generated + ret = append(ret, &plugin.Generated{ + Content: string(bytes), + Name: &filePath, + }) + + return ret +} + +func (g *OpenAPIGenerator) getDocumentOption(obj interface{}) error { + serviceOrStruct, name := g.getDocumentAnnotationInWhichServiceOrStruct() + if serviceOrStruct == consts.DocumentOptionServiceType { + serviceDesc := g.fileDesc.GetServiceDescriptor(name) + err := utils.ParseServiceOption(serviceDesc, consts.OpenapiDocument, obj) + if err != nil { + return err + } + } else if serviceOrStruct == consts.DocumentOptionStructType { + structDesc := g.fileDesc.GetStructDescriptor(name) + err := utils.ParseStructOption(structDesc, consts.OpenapiDocument, obj) + if err != nil { + return err + } + } + return nil +} + +func (g *OpenAPIGenerator) addPathsToDocument(d *openapi.Document, services []*thrift_reflection.ServiceDescriptor) { + var err error + for _, s := range services { + annotationsCount := 0 + for _, m := range s.GetMethods() { + var inputDesc, outputDesc, throwDesc *thrift_reflection.StructDescriptor + + if len(m.Args) > 0 { + if len(m.Args) > 1 { + logs.Warnf("function '%s' has more than one argument, but only the first can be used in plugin now", m.GetName()) + } + // TODO: support more argument types + if m.Args[0].GetType().IsStruct() { + inputDesc, err = m.Args[0].GetType().GetStructDescriptor() + if err != nil { + logs.Errorf("Error getting arguments descriptor: %s", err) + } + } else { + logs.Errorf("now only support struct type for input, but got %s", m.Args[0].GetType().GetName()) + } + } + + // TODO: support more response types + if m.Response.IsStruct() { + outputDesc, err = m.Response.GetStructDescriptor() + if err != nil { + logs.Errorf("Error getting response descriptor: %s", err) + } + } else if m.Response.Name != "void" { + logs.Errorf("now only support struct type for output, but got %s", m.Response.Name) + } + + if len(m.ThrowExceptions) > 0 { + throwDesc, err = m.ThrowExceptions[0].GetType().GetExceptionDescriptor() + if err != nil { + logs.Errorf("Error getting exception descriptor: %s", err) + } + } + var host string + + if urls, ok := m.Annotations[consts.ApiBaseURL]; ok && len(urls) > 0 { + host = urls[0] + } else if domains, ok := s.Annotations[consts.ApiBaseDomain]; ok && len(domains) > 0 { + host = domains[0] + } + + annotationsCount++ + operationID := s.GetName() + "_" + m.GetName() + path := "/" + m.GetName() + comment := g.filterCommentString(m.Comments) + + op, path2 := g.buildOperation(d, comment, operationID, s.GetName(), path, host, inputDesc, outputDesc, throwDesc) + + newOp := &openapi.Operation{} + err = utils.ParseMethodOption(m, consts.OpenapiOperation, &newOp) + if err != nil { + logs.Errorf("Error parsing method option: %s", err) + } + err = common.MergeStructs(op, newOp) + if err != nil { + logs.Errorf("Error merging method option: %s", err) + } + + g.addOperationToDocument(d, op, path2) + } + if annotationsCount > 0 { + comment := g.filterCommentString(s.Comments) + d.Tags = append(d.Tags, &openapi.Tag{Name: s.GetName(), Description: comment}) + } + } +} + +func (g *OpenAPIGenerator) buildOperation( + d *openapi.Document, + description string, + operationID string, + tagName string, + path string, + host string, + inputDesc *thrift_reflection.StructDescriptor, + outputDesc *thrift_reflection.StructDescriptor, + throwDesc *thrift_reflection.StructDescriptor, +) (*openapi.Operation, string) { + // Parameters array to hold all parameter objects + var parameters []*openapi.ParameterOrReference + + fieldSchema := &openapi.SchemaOrReference{ + Schema: &openapi.Schema{ + Type: consts.SchemaObjectType, + }, + } + parameter := &openapi.Parameter{ + Name: consts.ParameterNameTTHeader, + In: consts.ParameterInQuery, + Description: consts.ParameterDescription, + Required: false, + Schema: fieldSchema, + } + parameters = append(parameters, &openapi.ParameterOrReference{ + Parameter: parameter, + }) + + var RequestBody *openapi.RequestBodyOrReference + + if inputDesc != nil { + bodySchema := g.getSchemaByOption(inputDesc) + + var additionalProperties []*openapi.NamedMediaType + if len(bodySchema.Properties.AdditionalProperties) > 0 { + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeJSON, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Schema: bodySchema, + }, + }, + }) + } + + if len(additionalProperties) > 0 { + RequestBody = &openapi.RequestBodyOrReference{ + RequestBody: &openapi.RequestBody{ + Description: g.filterCommentString(inputDesc.Comments), + Content: &openapi.MediaTypes{ + AdditionalProperties: additionalProperties, + }, + }, + } + } + } + + var ( + desc string + contentOrEmpty *openapi.MediaTypes + exceptionDesc string + exceptionContentOrEmpty *openapi.MediaTypes + responses *openapi.Responses + ) + + if outputDesc != nil { + name, content := g.getResponseForStruct(d, outputDesc) + desc = g.filterCommentString(outputDesc.Comments) + + if desc == "" { + desc = consts.DefaultResponseDesc + } + + if len(content.AdditionalProperties) != 0 { + contentOrEmpty = content + } + + if contentOrEmpty != nil || exceptionContentOrEmpty != nil { + responses = &openapi.Responses{ + ResponseOrReference: []*openapi.NamedResponseOrReference{ + { + Name: name, + Value: &openapi.ResponseOrReference{ + Response: &openapi.Response{ + Description: desc, + Content: contentOrEmpty, + }, + }, + }, + }, + } + } + } + + if throwDesc != nil { + exceptionName, exceptionContent := g.getExceptionForStruct(d, throwDesc) + exceptionDesc = g.filterCommentString(throwDesc.Comments) + + if exceptionDesc == "" { + exceptionDesc = consts.DefaultExceptionDesc + } + + if len(exceptionContent.AdditionalProperties) != 0 { + exceptionContentOrEmpty = exceptionContent + } + + if responses == nil { + responses = &openapi.Responses{ + ResponseOrReference: []*openapi.NamedResponseOrReference{}, + } + } + + if contentOrEmpty != nil || exceptionContentOrEmpty != nil { + responses = &openapi.Responses{ + ResponseOrReference: append(responses.ResponseOrReference, &openapi.NamedResponseOrReference{ + Name: exceptionName, + Value: &openapi.ResponseOrReference{ + Response: &openapi.Response{ + Description: exceptionDesc, + Content: exceptionContentOrEmpty, + }, + }, + }), + } + } + } + + re := regexp.MustCompile(`:(\w+)`) + path = re.ReplaceAllString(path, `{$1}`) + + op := &openapi.Operation{ + Tags: []string{tagName}, + Description: description, + OperationID: operationID, + Parameters: parameters, + Responses: responses, + RequestBody: RequestBody, + } + + if host != "" { + if !strings.HasPrefix(host, consts.URLDefaultPrefixHTTP) && !strings.HasPrefix(host, consts.URLDefaultPrefixHTTPS) { + host = consts.URLDefaultPrefixHTTP + host + } + op.Servers = append(op.Servers, &openapi.Server{URL: host}) + } + + return op, path +} + +func (g *OpenAPIGenerator) getDocumentAnnotationInWhichServiceOrStruct() (string, string) { + var ret string + for _, s := range g.ast.Services { + v := s.Annotations.Get(consts.OpenapiDocument) + if len(v) > 0 { + ret = s.GetName() + return consts.DocumentOptionServiceType, ret + } + } + for _, s := range g.ast.Structs { + v := s.Annotations.Get(consts.OpenapiDocument) + if len(v) > 0 { + ret = s.GetName() + return consts.DocumentOptionStructType, ret + } + } + return "", ret +} + +func (g *OpenAPIGenerator) getResponseForStruct(d *openapi.Document, desc *thrift_reflection.StructDescriptor) (string, *openapi.MediaTypes) { + bodySchema := g.getSchemaByOption(desc) + + var additionalProperties []*openapi.NamedMediaType + + if len(bodySchema.Properties.AdditionalProperties) > 0 { + refSchema := &openapi.NamedSchemaOrReference{ + Name: desc.GetName(), + Value: &openapi.SchemaOrReference{Schema: bodySchema}, + } + ref := consts.ComponentSchemaPrefix + desc.GetName() + g.addSchemaToDocument(d, refSchema) + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeJSON, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Reference: &openapi.Reference{Xref: ref}, + }, + }, + }) + } + + content := &openapi.MediaTypes{ + AdditionalProperties: additionalProperties, + } + + return consts.StatusOK, content +} + +func (g *OpenAPIGenerator) getExceptionForStruct(d *openapi.Document, desc *thrift_reflection.StructDescriptor) (string, *openapi.MediaTypes) { + bodySchema := g.getSchemaByOption(desc) + + var additionalProperties []*openapi.NamedMediaType + + if len(bodySchema.Properties.AdditionalProperties) > 0 { + refSchema := &openapi.NamedSchemaOrReference{ + Name: desc.GetName(), + Value: &openapi.SchemaOrReference{Schema: bodySchema}, + } + ref := consts.ComponentSchemaPrefix + desc.GetName() + g.addSchemaToDocument(d, refSchema) + additionalProperties = append(additionalProperties, &openapi.NamedMediaType{ + Name: consts.ContentTypeJSON, + Value: &openapi.MediaType{ + Schema: &openapi.SchemaOrReference{ + Reference: &openapi.Reference{Xref: ref}, + }, + }, + }) + } + + content := &openapi.MediaTypes{ + AdditionalProperties: additionalProperties, + } + + return consts.StatusBadRequest, content +} + +func (g *OpenAPIGenerator) getSchemaByOption(inputDesc *thrift_reflection.StructDescriptor) *openapi.Schema { + definitionProperties := &openapi.Properties{ + AdditionalProperties: make([]*openapi.NamedSchemaOrReference, 0), + } + + var allRequired []string + var extSchema *openapi.Schema + err := utils.ParseStructOption(inputDesc, consts.OpenapiSchema, &extSchema) + if err != nil { + logs.Errorf("Error parsing struct option: %s", err) + } + if extSchema != nil { + if extSchema.Required != nil { + allRequired = extSchema.Required + } + } + + var required []string + for _, field := range inputDesc.GetFields() { + extName := field.GetName() + + if common.Contains(allRequired, extName) { + required = append(required, extName) + } + + // Get the field description from the comments. + description := g.filterCommentString(field.Comments) + fieldSchema := g.schemaOrReferenceForField(field.Type) + if fieldSchema == nil { + continue + } + + if fieldSchema.IsSetSchema() { + fieldSchema.Schema.Description = description + newFieldSchema := &openapi.Schema{} + err := utils.ParseFieldOption(field, consts.OpenapiProperty, &newFieldSchema) + if err != nil { + logs.Errorf("Error parsing field option: %s", err) + } + err = common.MergeStructs(fieldSchema.Schema, newFieldSchema) + if err != nil { + logs.Errorf("Error merging field option: %s", err) + } + } + + definitionProperties.AdditionalProperties = append( + definitionProperties.AdditionalProperties, + &openapi.NamedSchemaOrReference{ + Name: extName, + Value: fieldSchema, + }, + ) + } + + schema := &openapi.Schema{ + Type: consts.SchemaObjectType, + Properties: definitionProperties, + } + + if extSchema != nil { + err := common.MergeStructs(schema, extSchema) + if err != nil { + logs.Errorf("Error merging struct option: %s", err) + } + } + + schema.Required = required + return schema +} + +// filterCommentString removes linter rules from comments. +func (g *OpenAPIGenerator) filterCommentString(str string) string { + var comments []string + matches := regexp.MustCompile(consts.CommentPatternRegexp).FindAllStringSubmatch(str, -1) + + for _, match := range matches { + if match[1] != "" { + // Handle one-line comments + comments = append(comments, strings.TrimSpace(match[1])) + } else if match[2] != "" { + // Handle multiline comments + multiLineComment := match[2] + lines := strings.Split(multiLineComment, "\n") + + // Find the minimum indentation level (excluding empty lines) + minIndent := -1 + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if trimmedLine != "" { + lineIndent := len(line) - len(strings.TrimLeft(line, " ")) + if minIndent == -1 || lineIndent < minIndent { + minIndent = lineIndent + } + } + } + + // Remove the minimum indentation and any leading '*' from each line + for i, line := range lines { + if minIndent > 0 && len(line) >= minIndent { + line = line[minIndent:] + } + lines[i] = strings.TrimPrefix(line, "*") + } + + // Remove leading and trailing empty lines from the comment block + comments = append(comments, strings.TrimSpace(strings.Join(lines, "\n"))) + } + } + + return strings.Join(comments, "\n") +} + +func (g *OpenAPIGenerator) addSchemasForStructsToDocument(d *openapi.Document, structs []*thrift_reflection.StructDescriptor) { + for _, s := range structs { + var sls []*thrift_reflection.StructDescriptor + for _, f := range s.GetFields() { + fieldType := f.GetType() + if fieldType == nil { + logs.Errorf("Warning: field type is nil for field: %s\n", f.GetName()) + continue + } + if fieldType.IsStruct() { + structDesc, _ := fieldType.GetStructDescriptor() + sls = append(sls, structDesc) + } + } + if len(sls) > 0 { + g.addSchemasForStructsToDocument(d, sls) + } + + schemaName := s.GetName() + + // Only generate this if we need it and haven't already generated it. + if !common.Contains(g.requiredSchemas, schemaName) || + common.Contains(g.generatedSchemas, schemaName) { + continue + } + + // Get the description from the comments. + messageDescription := g.filterCommentString(s.Comments) + + // Build an array holding the fields of the message. + definitionProperties := &openapi.Properties{ + AdditionalProperties: make([]*openapi.NamedSchemaOrReference, 0), + } + + for _, field := range s.Fields { + // Get the field description from the comments. + description := g.filterCommentString(field.Comments) + fieldSchema := g.schemaOrReferenceForField(field.Type) + if fieldSchema == nil { + continue + } + + if fieldSchema.IsSetSchema() { + fieldSchema.Schema.Description = description + newFieldSchema := &openapi.Schema{} + err := utils.ParseFieldOption(field, consts.OpenapiProperty, &newFieldSchema) + if err != nil { + logs.Errorf("Error parsing field option: %s", err) + } + err = common.MergeStructs(fieldSchema.Schema, newFieldSchema) + if err != nil { + logs.Errorf("Error merging field option: %s", err) + } + } + + fName := field.GetName() + + definitionProperties.AdditionalProperties = append( + definitionProperties.AdditionalProperties, + &openapi.NamedSchemaOrReference{ + Name: fName, + Value: fieldSchema, + }, + ) + } + + schema := &openapi.Schema{ + Type: consts.SchemaObjectType, + Description: messageDescription, + Properties: definitionProperties, + } + + var extSchema *openapi.Schema + err := utils.ParseStructOption(s, consts.OpenapiSchema, &extSchema) + if err != nil { + logs.Errorf("Error parsing struct option: %s", err) + } + if extSchema != nil { + err = common.MergeStructs(schema, extSchema) + if err != nil { + logs.Errorf("Error merging struct option: %s", err) + } + } + + // Add the schema to the components.schema list. + g.addSchemaToDocument(d, &openapi.NamedSchemaOrReference{ + Name: schemaName, + Value: &openapi.SchemaOrReference{ + Schema: schema, + }, + }) + } +} + +// addSchemaToDocument adds the schema to the document if required +func (g *OpenAPIGenerator) addSchemaToDocument(d *openapi.Document, schema *openapi.NamedSchemaOrReference) { + if common.Contains(g.generatedSchemas, schema.Name) { + return + } + g.generatedSchemas = append(g.generatedSchemas, schema.Name) + d.Components.Schemas.AdditionalProperties = append(d.Components.Schemas.AdditionalProperties, schema) +} + +func (g *OpenAPIGenerator) addOperationToDocument(d *openapi.Document, op *openapi.Operation, path string) { + var selectedPathItem *openapi.NamedPathItem + for _, namedPathItem := range d.Paths.Path { + if namedPathItem.Name == path { + selectedPathItem = namedPathItem + break + } + } + // If we get here, we need to create a path item. + if selectedPathItem == nil { + selectedPathItem = &openapi.NamedPathItem{Name: path, Value: &openapi.PathItem{}} + d.Paths.Path = append(d.Paths.Path, selectedPathItem) + } + + selectedPathItem.Value.Post = op +} + +func (g *OpenAPIGenerator) schemaReferenceForMessage(message *thrift_reflection.StructDescriptor) string { + schemaName := message.GetName() + if !common.Contains(g.requiredSchemas, schemaName) { + g.requiredSchemas = append(g.requiredSchemas, schemaName) + g.requiredTypeDesc = append(g.requiredTypeDesc, message) + } + return consts.ComponentSchemaPrefix + schemaName +} + +func (g *OpenAPIGenerator) schemaOrReferenceForField(fieldType *thrift_reflection.TypeDescriptor) *openapi.SchemaOrReference { + var kindSchema *openapi.SchemaOrReference + + switch { + case fieldType.IsStruct(): + structDesc, err := fieldType.GetStructDescriptor() + if err != nil { + logs.Errorf("Error getting struct descriptor: %s", err) + return nil + } + ref := g.schemaReferenceForMessage(structDesc) + kindSchema = &openapi.SchemaOrReference{ + Reference: &openapi.Reference{Xref: ref}, + } + + case fieldType.IsMap(): + valueSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) + kindSchema = &openapi.SchemaOrReference{ + Schema: &openapi.Schema{ + Type: consts.SchemaObjectType, + AdditionalProperties: &openapi.AdditionalPropertiesItem{ + SchemaOrReference: valueSchema, + }, + }, + } + + case fieldType.IsList(): + itemSchema := g.schemaOrReferenceForField(fieldType.GetValueType()) + kindSchema = &openapi.SchemaOrReference{ + Schema: &openapi.Schema{ + Type: "array", + Items: &openapi.ItemsItem{ + SchemaOrReference: []*openapi.SchemaOrReference{itemSchema}, + }, + }, + } + case fieldType.IsTypedef(): + typedefDesc, err := fieldType.GetTypedefDescriptor() + if err != nil { + logs.Errorf("Error getting typedef descriptor: %s", err) + return nil + } + kindSchema = g.schemaOrReferenceForField(typedefDesc.Type) + + case fieldType.IsEnum(): + enumDesc, err := fieldType.GetEnumDescriptor() + if err != nil { + logs.Errorf("Error getting enum descriptor: %s", err) + return nil + } + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "enum" + kindSchema.Schema.Enum = make([]*openapi.Any, 0, len(enumDesc.GetValues())) + for _, v := range enumDesc.GetValues() { + kindSchema.Schema.Enum = append(kindSchema.Schema.Enum, &openapi.Any{Yaml: v.GetName()}) + } + + case fieldType.IsUnion(): + unionDesc, err := fieldType.GetUnionDescriptor() + if err != nil { + logs.Errorf("Error getting union descriptor: %s", err) + return nil + } + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + kindSchema.Schema.OneOf = make([]*openapi.SchemaOrReference, 0, len(unionDesc.GetFields())) + for _, f := range unionDesc.GetFields() { + fieldSchema := g.schemaOrReferenceForField(f.Type) + kindSchema.Schema.OneOf = append(kindSchema.Schema.OneOf, fieldSchema) + } + + case fieldType.IsException(): + logs.Errorf("Error: exception type not supported: %s for field", fieldType.GetName()) + + default: + kindSchema = &openapi.SchemaOrReference{Schema: &openapi.Schema{}} + switch fieldType.GetName() { + case "string": + kindSchema.Schema.Type = "string" + case "binary": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "binary" + case "bool": + kindSchema.Schema.Type = "boolean" + case "byte": + kindSchema.Schema.Type = "string" + kindSchema.Schema.Format = "byte" + case "double": + kindSchema.Schema.Type = "number" + kindSchema.Schema.Format = "double" + case "i8": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int8" + case "i16": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int16" + case "i32": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int32" + case "i64": + kindSchema.Schema.Type = "integer" + kindSchema.Schema.Format = "int64" + } + } + + return kindSchema +} diff --git a/thrift-gen-rpc-swagger/generator/server_gen.go b/thrift-gen-rpc-swagger/generator/server_gen.go new file mode 100644 index 0000000..b6da9da --- /dev/null +++ b/thrift-gen-rpc-swagger/generator/server_gen.go @@ -0,0 +1,124 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package generator + +import ( + "bytes" + "errors" + "fmt" + "io/ioutil" + "path/filepath" + "regexp" + "strings" + "text/template" + + "github.com/cloudwego/thriftgo/parser" + "github.com/cloudwego/thriftgo/plugin" + "github.com/hertz-contrib/swagger-generate/common/consts" + "github.com/hertz-contrib/swagger-generate/common/tpl" + "github.com/hertz-contrib/swagger-generate/common/utils" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/args" +) + +type ServerGenerator struct { + IdlPath string + KitexAddr string + OutputDir string +} + +func NewServerGenerator(ast *parser.Thrift, args *args.Arguments) (*ServerGenerator, error) { + defaultKitexAddr := consts.DefaultKitexAddr + defaultOutputDir := consts.DefaultOutputDir + + idlPath := ast.Filename + if idlPath == "" { + return nil, errors.New("failed to get Thrift file path") + } + + kitexAddr := args.KitexAddr + if kitexAddr == "" { + kitexAddr = defaultKitexAddr + } + + outputDir := args.OutputDir + if outputDir == "" { + outputDir = defaultOutputDir + } + + if err := validateAddress(kitexAddr); err != nil { + return nil, err + } + + return &ServerGenerator{ + IdlPath: idlPath, + KitexAddr: kitexAddr, + OutputDir: outputDir, + }, nil +} + +func (g *ServerGenerator) Generate() ([]*plugin.Generated, error) { + filePath := filepath.Join(g.OutputDir, consts.DefaultOutputSwaggerFile) + + if utils.FileExists(filePath) { + updatedContent, err := updateVariables(filePath, g.KitexAddr, g.IdlPath) + if err != nil { + return nil, err + } + return []*plugin.Generated{{ + Content: updatedContent, + Name: &filePath, + }}, nil + } + + tmpl, err := template.New("server").Delims("{{", "}}").Parse(consts.CodeGenerationCommentThriftRpc + "\n" + tpl.ServerTemplateRpc) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + err = tmpl.Execute(&buf, g) + if err != nil { + return nil, err + } + + return []*plugin.Generated{{ + Content: buf.String(), + Name: &filePath, + }}, nil +} + +func updateVariables(filePath, newKitexAddr, newIdlPath string) (string, error) { + content, err := ioutil.ReadFile(filePath) + if err != nil { + return "", err + } + + kitexAddrPattern := regexp.MustCompile(`kitexAddr\s*=\s*"(.*?)"`) + idlPathPattern := regexp.MustCompile(`idlFile\s*=\s*"(.*?)"`) + + updatedContent := kitexAddrPattern.ReplaceAllString(string(content), fmt.Sprintf(`kitexAddr = "%s"`, newKitexAddr)) + updatedContent = idlPathPattern.ReplaceAllString(updatedContent, fmt.Sprintf(`idlFile = "%s"`, newIdlPath)) + + return updatedContent, nil +} + +func validateAddress(addr string) error { + if !strings.Contains(addr, ":") { + return errors.New("address must include a port (e.g., '127.0.0.1:8888')") + } + return nil +} diff --git a/thrift-gen-rpc-swagger/go.mod b/thrift-gen-rpc-swagger/go.mod new file mode 100644 index 0000000..66b9eb8 --- /dev/null +++ b/thrift-gen-rpc-swagger/go.mod @@ -0,0 +1,17 @@ +module github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger + +go 1.16 + +require ( + github.com/bytedance/gopkg v0.0.0-20240514070511-01b2cbcf35e1 + github.com/cloudwego/hertz v0.9.2 + github.com/cloudwego/hertz/cmd/hz v0.9.1 + github.com/cloudwego/kitex v0.10.3 + github.com/cloudwego/thriftgo v0.3.15 + github.com/hertz-contrib/cors v0.1.0 + github.com/hertz-contrib/swagger v0.1.0 + github.com/hertz-contrib/swagger-generate v0.0.0 + github.com/swaggo/files v1.0.1 +) + +replace github.com/hertz-contrib/swagger-generate => ../../swagger-generate diff --git a/thrift-gen-rpc-swagger/go.sum b/thrift-gen-rpc-swagger/go.sum new file mode 100644 index 0000000..fa78902 --- /dev/null +++ b/thrift-gen-rpc-swagger/go.sum @@ -0,0 +1,533 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= +github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= +github.com/apache/thrift v0.13.0 h1:5hryIiq9gtn+MiLVn0wP37kb/uTeRZgN08WoCsAhIhI= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/bytedance/go-tagexpr/v2 v2.9.2 h1:QySJaAIQgOEDQBLS3x9BxOWrnhqu5sQ+f6HaZIxD39I= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/gopkg v0.0.0-20230728082804-614d0af6619b/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/gopkg v0.0.0-20240507064146-197ded923ae3/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/gopkg v0.0.0-20240514070511-01b2cbcf35e1 h1:rT7Mm6uUpHeZQzfs2v0Mlj0SL02CzyVi+EB7VYPM/z4= +github.com/bytedance/gopkg v0.0.0-20240514070511-01b2cbcf35e1/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/mockey v1.2.1 h1:g84ngI88hz1DR4wZTL3yOuqlEcq67MretBfQUdXwrmw= +github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94zf2JZ3X4= +github.com/bytedance/sonic v1.3.0/go.mod h1:V973WhNhGmvHxW6nQmsHEfHaoU9F3zTF+93rH03hcUQ= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic v1.11.8 h1:Zw/j1KfiS+OYTi9lyB3bb0CFxPJVkM17k1wyDG32LRA= +github.com/bytedance/sonic v1.11.8/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/configmanager v0.2.2 h1:sVrJB8gWYTlPV2OS3wcgJSO9F2/9Zbkmcm1Z7jempOU= +github.com/cloudwego/configmanager v0.2.2/go.mod h1:ppiyU+5TPLonE8qMVi/pFQk2eL3Q4P7d4hbiNJn6jwI= +github.com/cloudwego/dynamicgo v0.2.9 h1:MHGyGmdFT8iMOsM5S9iutjZB0csu2LupsTTHyi6a8pY= +github.com/cloudwego/dynamicgo v0.2.9/go.mod h1:F3jlbPmlNzhcuDMXwZoBJ7rJKpg2iE+TnIy9pSJiGzs= +github.com/cloudwego/fastpb v0.0.4 h1:/ROVVfoFtpfc+1pkQLzGs+azjxUbSOsAqSY4tAAx4mg= +github.com/cloudwego/fastpb v0.0.4/go.mod h1:/V13XFTq2TUkxj2qWReV8MwfPC4NnPcy6FsrojnsSG0= +github.com/cloudwego/frugal v0.1.15 h1:LC55UJKhQPMFVjDPbE+LJcF7etZjSx6uokG1tk0wPK0= +github.com/cloudwego/frugal v0.1.15/go.mod h1:26kU1r18vA8vRg12c66XPDlfv1GQHDbE1RpusipXfcI= +github.com/cloudwego/hertz v0.0.1/go.mod h1:prTyExvsH/UmDkvfU3dp3EHsZFQISfT8R7BirvpTKdo= +github.com/cloudwego/hertz v0.6.2/go.mod h1:2em2hGREvCBawsTQcQxyWBGVlCeo+N1pp2q0HkkbwR0= +github.com/cloudwego/hertz v0.9.2 h1:VbqddZ5RuvcgxzfxvXcmTiRisGYoo0+WnHGeDJKhjqI= +github.com/cloudwego/hertz v0.9.2/go.mod h1:cs8dH6unM4oaJ5k9m6pqbgLBPqakGWMG0+cthsxitsg= +github.com/cloudwego/hertz/cmd/hz v0.9.1 h1:v75TueFIZhTgYYnoM+6VxKHu58ZS3HJ+Qp4T07UYcKk= +github.com/cloudwego/hertz/cmd/hz v0.9.1/go.mod h1:6SroAwvZkyL54CiPANDkTR3YoX2MY4ZOW1+gtmWhRJE= +github.com/cloudwego/iasm v0.0.9/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/kitex v0.10.3 h1:L3JRkb25oXMf1ROslQNY7E9EpaUBBjJWwjGfkQERJ+k= +github.com/cloudwego/kitex v0.10.3/go.mod h1:6wYnJc0TpKnHwM8/Fcy2YrQNyrlmpMYP0y5ADZrqYsc= +github.com/cloudwego/localsession v0.0.2 h1:N9/IDtCPj1fCL9bCTP+DbXx3f40YjVYWcwkJG0YhQkY= +github.com/cloudwego/localsession v0.0.2/go.mod h1:kiJxmvAcy4PLgKtEnPS5AXed3xCiXcs7Z+KBHP72Wv8= +github.com/cloudwego/netpoll v0.2.4/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.6.0/go.mod h1:xVefXptcyheopwNDZjDPcfU6kIjZXZ4nY550k1yH9eQ= +github.com/cloudwego/netpoll v0.6.3 h1:t+ndlwBFjQZimUj3ul31DwI45t18eOr2pcK3juZZm+E= +github.com/cloudwego/netpoll v0.6.3/go.mod h1:kaqvfZ70qd4T2WtIIpCOi5Cxyob8viEpzLhCrTrz3HM= +github.com/cloudwego/runtimex v0.1.0 h1:HG+WxWoj5/CDChDZ7D99ROwvSMkuNXAqt6hnhTTZDiI= +github.com/cloudwego/runtimex v0.1.0/go.mod h1:23vL/HGV0W8nSCHbe084AgEBdDV4rvXenEUMnUNvUd8= +github.com/cloudwego/thriftgo v0.1.7/go.mod h1:LzeafuLSiHA9JTiWC8TIMIq64iadeObgRUhmVG1OC/w= +github.com/cloudwego/thriftgo v0.3.6/go.mod h1:29ukiySoAMd0vXMYIduAY9dph/7dmChvOS11YLotFb8= +github.com/cloudwego/thriftgo v0.3.15 h1:yB/DDGjeSjliyidMVBjKhGl9RgE4M8iVIz5dKpAIyUs= +github.com/cloudwego/thriftgo v0.3.15/go.mod h1:R4a+4aVDI0V9YCTfpNgmvbkq/9ThKgF7Om8Z0I36698= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= +github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= +github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= +github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= +github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/goccy/go-json v0.9.4/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3 h1:mpL/HvfIgIejhVwAfxBQkwEjlhP5o0O9RAeTAjpwzxc= +github.com/google/pprof v0.0.0-20220608213341-c488b8fa1db3/go.mod h1:gSuNB+gJaOiQKLEZ+q+PK9Mq3SOzhRcw2GsGS/FhYDk= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 h1:yE9ULgp02BhYIrO6sdV/FPe0xQM6fNHkVQW2IAymfM0= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/hertz-contrib/cors v0.1.0 h1:PQ5mATygSMzTlYtfyMyHjobYoJeHKe2Qt3tcAOgbI6E= +github.com/hertz-contrib/cors v0.1.0/go.mod h1:VPReoq+Rvu/lZOfpp5CcX3x4mpZUc3EpSXBcVDcbvOc= +github.com/hertz-contrib/swagger v0.1.0 h1:FlnMPRHuvAt/3pt3KCQRZ6RH1g/agma9SU70Op2Pb58= +github.com/hertz-contrib/swagger v0.1.0/go.mod h1:Bt5i+Nyo7bGmYbuEfMArx7raf1oK+nWVgYbEvhpICKE= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI= +github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ= +github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= +github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E= +github.com/jhump/protoreflect v1.12.0 h1:1NQ4FpWMgn3by/n1X0fbeKEUxP1wBt7+Oitpv01HR10= +github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/gls v0.0.0-20220109145502-612d0167dce5 h1:uiS4zKYKJVj5F3ID+5iylfKPsEQmBEOucSD9Vgmn0i0= +github.com/modern-go/gls v0.0.0-20220109145502-612d0167dce5/go.mod h1:I8AX+yW//L8Hshx6+a1m3bYkwXkpsVjA2795vP4f4oQ= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= +github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +github.com/oleiade/lane v1.0.1 h1:hXofkn7GEOubzTwNpeL9MaNy8WxolCYb9cInAIeqShU= +github.com/oleiade/lane v1.0.1/go.mod h1:IyTkraa4maLfjq/GmHR+Dxb4kCMtEGeb+qmhlrQ5Mk4= +github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= +github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/swaggo/files v0.0.0-20210815190702-a29dd2bc99b2/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= +github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE= +github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg= +github.com/swaggo/swag v1.16.1 h1:fTNRhKstPKxcnoKsytm4sahr8FaYzUcT7i1/3nd/fBg= +github.com/swaggo/swag v1.16.1/go.mod h1:9/LMvHycG3NFHfR6LwvikHv5iFvmPADQ359cKikGxto= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.4/go.mod h1:098SZ494YoMWPmMO6ct4dcFnqxwj9r/gF0Etp19pSNM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/urfave/cli/v2 v2.23.0/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.2.0 h1:W1sUEHXiJTfjaFJ5SLo0N6lZn+0eO5gWD1MFeTGqQEY= +golang.org/x/arch v0.2.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE= +golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190927191325-030b2cf1153e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= +gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= +gonum.org/v1/gonum v0.12.0/go.mod h1:73TDxJfAAHeA8Mk9mf8NlIppyhQNo5GLTcYeqgo2lvY= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= +gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= +gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384 h1:z+j74wi4yV+P7EtK9gPLGukOk7mFOy9wMQaC0wNb7eY= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/thrift-gen-rpc-swagger/main.go b/thrift-gen-rpc-swagger/main.go new file mode 100644 index 0000000..ad528dd --- /dev/null +++ b/thrift-gen-rpc-swagger/main.go @@ -0,0 +1,44 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "os" + + "github.com/cloudwego/hertz/cmd/hz/util/logs" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/plugins" +) + +func main() { + var queryVersion bool + + f := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) + f.BoolVar(&queryVersion, "version", false, "Show the version of thrift-gen-rpc-swagger") + + if err := f.Parse(os.Args[1:]); err != nil { + logs.Error("Failed to parse flags: %v", err) + os.Exit(2) + } + + if queryVersion { + println(plugins.Version) + os.Exit(0) + } + + os.Exit(plugins.Run()) +} diff --git a/thrift-gen-rpc-swagger/plugins/plugins.go b/thrift-gen-rpc-swagger/plugins/plugins.go new file mode 100644 index 0000000..a33ad1c --- /dev/null +++ b/thrift-gen-rpc-swagger/plugins/plugins.go @@ -0,0 +1,96 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package plugins + +import ( + "errors" + "io" + "os" + + "github.com/cloudwego/hertz/cmd/hz/util/logs" + "github.com/cloudwego/thriftgo/plugin" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/args" + "github.com/hertz-contrib/swagger-generate/thrift-gen-rpc-swagger/generator" +) + +func Run() int { + data, err := io.ReadAll(os.Stdin) + if err != nil { + logs.Errorf("Failed to get input: %v", err.Error()) + os.Exit(1) + } + + req, err := plugin.UnmarshalRequest(data) + if err != nil { + logs.Errorf("Failed to unmarshal request: %v", err.Error()) + os.Exit(1) + } + + if err := handleRequest(req); err != nil { + logs.Errorf("Failed to handle request: %v", err.Error()) + os.Exit(1) + } + + os.Exit(0) + return 0 +} + +func handleRequest(req *plugin.Request) (err error) { + if req == nil { + return errors.New("request is nil") + } + + args := new(args.Arguments) + if err = args.Unpack(req.PluginParameters); err != nil { + return err + } + + ast := req.GetAST() + + og := generator.NewOpenAPIGenerator(ast) + openapiContent := og.BuildDocument(args) + + sg, err := generator.NewServerGenerator(ast, args) + if err != nil { + return err + } + serverContent, err := sg.Generate() + if err != nil { + return err + } + + res := &plugin.Response{ + Contents: append(openapiContent, serverContent...), + } + if err = handleResponse(res); err != nil { + return err + } + + return err +} + +func handleResponse(res *plugin.Response) error { + data, err := plugin.MarshalResponse(res) + if err != nil { + return err + } + _, err = os.Stdout.Write(data) + if err != nil { + return err + } + return nil +} diff --git a/thrift-gen-rpc-swagger/plugins/version.go b/thrift-gen-rpc-swagger/plugins/version.go new file mode 100644 index 0000000..de164bf --- /dev/null +++ b/thrift-gen-rpc-swagger/plugins/version.go @@ -0,0 +1,19 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package plugins + +const Version = "v0.1.0" diff --git a/thrift-gen-rpc-swagger/utils/utils.go b/thrift-gen-rpc-swagger/utils/utils.go new file mode 100644 index 0000000..02d32b4 --- /dev/null +++ b/thrift-gen-rpc-swagger/utils/utils.go @@ -0,0 +1,113 @@ +/* + * Copyright 2024 CloudWeGo Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "encoding/json" + "errors" + + "github.com/cloudwego/thriftgo/extension/thrift_option" + "github.com/cloudwego/thriftgo/thrift_reflection" +) + +func ParseStructOption(descriptor *thrift_reflection.StructDescriptor, optionName string, obj interface{}) error { + opt, err := thrift_option.ParseStructOption(descriptor, optionName) + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { + return nil + } + if err != nil { + return err + } + mapVal := opt.GetValue() + mapValMap := mapVal.(map[string]interface{}) + jsonData, err := json.Marshal(mapValMap) + if err != nil { + return err + } + if err = json.Unmarshal(jsonData, obj); err != nil { + return err + } + return err +} + +func ParseServiceOption(descriptor *thrift_reflection.ServiceDescriptor, optionName string, obj interface{}) error { + opt, err := thrift_option.ParseServiceOption(descriptor, optionName) + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { + return nil + } + if err != nil { + return err + } + mapVal := opt.GetValue() + mapValMap := mapVal.(map[string]interface{}) + jsonData, err := json.Marshal(mapValMap) + if err != nil { + return err + } + if err = json.Unmarshal(jsonData, obj); err != nil { + return err + } + return err +} + +func ParseMethodOption(descriptor *thrift_reflection.MethodDescriptor, optionName string, obj interface{}) error { + opt, err := thrift_option.ParseMethodOption(descriptor, optionName) + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { + return nil + } + if err != nil { + return err + } + mapVal := opt.GetValue() + mapValMap := mapVal.(map[string]interface{}) + jsonData, err := json.Marshal(mapValMap) + if err != nil { + return err + } + if err = json.Unmarshal(jsonData, obj); err != nil { + return err + } + return err +} + +func ParseFieldOption(descriptor *thrift_reflection.FieldDescriptor, optionName string, obj interface{}) error { + opt, err := thrift_option.ParseFieldOption(descriptor, optionName) + if errors.Is(err, thrift_option.ErrKeyNotMatch) || + errors.Is(err, thrift_option.ErrNotIncluded) || + errors.Is(err, thrift_option.ErrNotExistOption) { + return nil + } + if err != nil { + return err + } + mapVal := opt.GetValue() + mapValMap := mapVal.(map[string]interface{}) + jsonData, err := json.Marshal(mapValMap) + if err != nil { + return err + } + if err = json.Unmarshal(jsonData, obj); err != nil { + return err + } + return err +}