gr_hz/generator/package_tpl.go

979 lines
23 KiB
Go

/*
* Copyright 2022 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
var (
routerTplName = "router.go"
middlewareTplName = "middleware.go"
middlewareSingleTplName = "middleware_single.go"
handlerTplName = "handler.go"
handlerSingleTplName = "handler_single.go"
modelTplName = "model.go"
registerTplName = "register.go"
clientTplName = "client.go" // generate a default client for server
hertzClientTplName = "hertz_client.go" // underlying client for client command
idlClientName = "idl_client.go" // client of service for quick call
insertPointNew = "//INSERT_POINT: DO NOT DELETE THIS LINE!"
insertPointPatternNew = `//INSERT_POINT\: DO NOT DELETE THIS LINE\!`
)
var templateNameSet = map[string]string{
routerTplName: routerTplName,
middlewareTplName: middlewareTplName,
middlewareSingleTplName: middlewareSingleTplName,
handlerTplName: handlerTplName,
handlerSingleTplName: handlerSingleTplName,
modelTplName: modelTplName,
registerTplName: registerTplName,
clientTplName: clientTplName,
hertzClientTplName: hertzClientTplName,
idlClientName: idlClientName,
}
func IsDefaultPackageTpl(name string) bool {
if _, exist := templateNameSet[name]; exist {
return true
}
return false
}
var defaultPkgConfig = TemplateConfig{
Layouts: []Template{
{
Path: defaultHandlerDir + sp + handlerTplName,
Delims: [2]string{"{{", "}}"},
Body: `// Code generated by hertz generator.
package {{.PackageName}}
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/protocol/consts"
{{- range $k, $v := .Imports}}
{{$k}} "{{$v.Package}}"
{{- end}}
)
{{range $_, $MethodInfo := .Methods}}
{{$MethodInfo.Comment}}
func {{$MethodInfo.Name}}(ctx context.Context, c *app.RequestContext) {
var err error
{{if ne $MethodInfo.RequestTypeName "" -}}
var req {{$MethodInfo.RequestTypeName}}
err = c.BindAndValidate(&req)
if err != nil {
c.String(consts.StatusBadRequest, err.Error())
return
}
{{end}}
resp := new({{$MethodInfo.ReturnTypeName}})
c.{{.Serializer}}(consts.StatusOK, resp)
}
{{end}}
`,
},
{
Path: defaultRouterDir + sp + routerTplName,
Delims: [2]string{"{{", "}}"},
Body: `// Code generated by hertz generator. DO NOT EDIT.
package {{$.PackageName}}
import (
"github.com/cloudwego/hertz/pkg/app/server"
{{- range $k, $v := .HandlerPackages}}
{{$k}} "{{$v}}"
{{- end}}
)
/*
This file will register all the routes of the services in the master idl.
And it will update automatically when you use the "update" command for the idl.
So don't modify the contents of the file, or your code will be deleted when it is updated.
*/
{{define "g"}}
{{- if eq .Path "/"}}r
{{- else}}{{.GroupName}}{{end}}
{{- end}}
{{define "G"}}
{{- if ne .Handler ""}}
{{- .GroupName}}.{{.HttpMethod}}("{{.Path}}", append({{.HandlerMiddleware}}Mw(), {{.Handler}})...)
{{- end}}
{{- if ne (len .Children) 0}}
{{.MiddleWare}} := {{template "g" .}}.Group("{{.Path}}", {{.GroupMiddleware}}Mw()...)
{{- end}}
{{- range $_, $router := .Children}}
{{- if ne .Handler ""}}
{{template "G" $router}}
{{- else}}
{ {{template "G" $router}}
}
{{- end}}
{{- end}}
{{- end}}
// Register register routes based on the IDL 'api.${HTTP Method}' annotation.
func Register{{$.IdlName}}(r *server.Hertz) {
{{template "G" .Router}}
}
`,
},
{
Path: defaultRouterDir + sp + registerTplName,
Body: `// Code generated by hertz generator. DO NOT EDIT.
package {{.PackageName}}
import (
"github.com/cloudwego/hertz/pkg/app/server"
{{$.DepPkgAlias}} "{{$.DepPkg}}"
)
// GeneratedRegister registers routers generated by IDL.
func GeneratedRegister(r *server.Hertz){
` + insertPointNew + `
{{$.RegisterName}}(r)
}
`,
},
// Model tpl is imported by model generator. Here only decides model directory.
{
Path: defaultModelDir + sp + modelTplName,
Body: ``,
},
{
Path: defaultRouterDir + sp + middlewareTplName,
Delims: [2]string{"{{", "}}"},
Body: `// Code generated by hertz generator.
package {{$.PackageName}}
import (
"github.com/cloudwego/hertz/pkg/app"
)
{{define "M"}}
{{- if ne .Children.Len 0}}
func {{.GroupMiddleware}}Mw() []app.HandlerFunc {
// your code...
return nil
}
{{end}}
{{- if ne .Handler ""}}
func {{.HandlerMiddleware}}Mw() []app.HandlerFunc {
// your code...
return nil
}
{{end}}
{{range $_, $router := $.Children}}{{template "M" $router}}{{end}}
{{- end}}
{{template "M" .Router}}
`,
},
{
Path: defaultClientDir + sp + clientTplName,
Delims: [2]string{"{{", "}}"},
Body: `// Code generated by hertz generator.
package {{$.PackageName}}
import (
"github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/common/config"
)
type {{.ServiceName}}Client struct {
client * client.Client
}
func New{{.ServiceName}}Client(opt ...config.ClientOption) (*{{.ServiceName}}Client, error) {
c, err := client.NewClient(opt...)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{
client: c,
}, nil
}
`,
},
{
Path: defaultHandlerDir + sp + handlerSingleTplName,
Delims: [2]string{"{{", "}}"},
Body: `
{{.Comment}}
func {{.Name}}(ctx context.Context, c *app.RequestContext) {
var err error
{{if ne .RequestTypeName "" -}}
var req {{.RequestTypeName}}
err = c.BindAndValidate(&req)
if err != nil {
c.String(consts.StatusBadRequest, err.Error())
return
}
{{end}}
resp := new({{.ReturnTypeName}})
c.{{.Serializer}}(consts.StatusOK, resp)
}
`,
},
{
Path: defaultRouterDir + sp + middlewareSingleTplName,
Delims: [2]string{"{{", "}}"},
Body: `
func {{.MiddleWare}}Mw() []app.HandlerFunc {
// your code...
return nil
}
`,
},
{
Path: defaultRouterDir + sp + hertzClientTplName,
Delims: [2]string{"{{", "}}"},
Body: hertzClientTpl,
},
{
Path: defaultRouterDir + sp + idlClientName,
Delims: [2]string{"{{", "}}"},
Body: idlClientTpl,
},
},
}
var hertzClientTpl = `// Code generated by hz.
package {{.PackageName}}
import (
"context"
"encoding/json"
"encoding/xml"
"fmt"
"io"
"net/http"
"net/url"
"reflect"
"regexp"
"strings"
hertz_client "github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/common/config"
"github.com/cloudwego/hertz/pkg/common/errors"
"github.com/cloudwego/hertz/pkg/protocol"
"github.com/cloudwego/hertz/pkg/protocol/client"
)
type use interface {
Use(mws ...hertz_client.Middleware)
}
// Definition of global data and types.
type ResponseResultDecider func(statusCode int, rawResponse *protocol.Response) (isError bool)
type (
bindRequestBodyFunc func(c *cli, r *request) (contentType string, body io.Reader, err error)
beforeRequestFunc func(*cli, *request) error
afterResponseFunc func(*cli, *response) error
)
var (
hdrContentTypeKey = http.CanonicalHeaderKey("Content-Type")
hdrContentEncodingKey = http.CanonicalHeaderKey("Content-Encoding")
plainTextType = "text/plain; charset=utf-8"
jsonContentType = "application/json; charset=utf-8"
formContentType = "multipart/form-data"
jsonCheck = regexp.MustCompile(` + "`(?i:(application|text)/(json|.*\\+json|json\\-.*)(; |$))`)\n" +
`xmlCheck = regexp.MustCompile(` + "`(?i:(application|text)/(xml|.*\\+xml)(; |$))`)\n" +
`
)
// Configuration of client
type Option struct {
f func(*Options)
}
type Options struct {
hostUrl string
enumAsInt bool
doer client.Doer
header http.Header
requestBodyBind bindRequestBodyFunc
responseResultDecider ResponseResultDecider
middlewares []hertz_client.Middleware
clientOption []config.ClientOption
}
func getOptions(ops ...Option) *Options {
opts := &Options{}
for _, do := range ops {
do.f(opts)
}
return opts
}
// WithHertzClientOption is used to pass configuration for the hertz client
func WithHertzClientOption(opt ...config.ClientOption) Option {
return Option{func(op *Options) {
op.clientOption = append(op.clientOption, opt...)
}}
}
// WithHertzClientMiddleware is used to register the middleware for the hertz client
func WithHertzClientMiddleware(mws ...hertz_client.Middleware) Option {
return Option{func(op *Options) {
op.middlewares = append(op.middlewares, mws...)
}}
}
// WithHertzClient is used to register a custom hertz client
func WithHertzClient(client client.Doer) Option {
return Option{func(op *Options) {
op.doer = client
}}
}
// WithHeader is used to add the default header, which is carried by every request
func WithHeader(header http.Header) Option {
return Option{func(op *Options) {
op.header = header
}}
}
// WithResponseResultDecider configure custom deserialization of http response to response struct
func WithResponseResultDecider(decider ResponseResultDecider) Option {
return Option{func(op *Options) {
op.responseResultDecider = decider
}}
}
// WithQueryEnumAsInt is used to set enum as int for query parameters
func WithQueryEnumAsInt(enable bool) Option {
return Option{func(op *Options) {
op.enumAsInt = enable
}}
}
func withHostUrl(HostUrl string) Option {
return Option{func(op *Options) {
op.hostUrl = HostUrl
}}
}
// underlying client
type cli struct {
hostUrl string
enumAsInt bool
doer client.Doer
header http.Header
bindRequestBody bindRequestBodyFunc
responseResultDecider ResponseResultDecider
beforeRequest []beforeRequestFunc
afterResponse []afterResponseFunc
}
func (c *cli) Use(mws ...hertz_client.Middleware) error {
u, ok := c.doer.(use)
if !ok {
return errors.NewPublic("doer does not support middleware, choose the right doer.")
}
u.Use(mws...)
return nil
}
func newClient(opts *Options) (*cli, error) {
if opts.requestBodyBind == nil {
opts.requestBodyBind = defaultRequestBodyBind
}
if opts.responseResultDecider == nil {
opts.responseResultDecider = defaultResponseResultDecider
}
if opts.doer == nil {
cli, err := hertz_client.NewClient(opts.clientOption...)
if err != nil {
return nil, err
}
opts.doer = cli
}
c := &cli{
hostUrl: opts.hostUrl,
enumAsInt: opts.enumAsInt,
doer: opts.doer,
header: opts.header,
bindRequestBody: opts.requestBodyBind,
responseResultDecider: opts.responseResultDecider,
beforeRequest: []beforeRequestFunc{
parseRequestURL,
parseRequestHeader,
createHTTPRequest,
},
afterResponse: []afterResponseFunc{
parseResponseBody,
},
}
if len(opts.middlewares) != 0 {
if err := c.Use(opts.middlewares...); err != nil {
return nil, err
}
}
return c, nil
}
func (c *cli) execute(req *request) (*response, error) {
var err error
for _, f := range c.beforeRequest {
if err = f(c, req); err != nil {
return nil, err
}
}
if hostHeader := req.header.Get("Host"); hostHeader != "" {
req.rawRequest.Header.SetHost(hostHeader)
}
resp := protocol.Response{}
err = c.doer.Do(req.ctx, req.rawRequest, &resp)
response := &response{
request: req,
rawResponse: &resp,
}
if err != nil {
return response, err
}
body, err := resp.BodyE()
if err != nil {
return nil, err
}
if strings.EqualFold(resp.Header.Get(hdrContentEncodingKey), "gzip") && resp.Header.ContentLength() != 0 {
body, err = resp.BodyGunzip()
if err != nil {
return nil, err
}
}
response.bodyByte = body
response.size = int64(len(response.bodyByte))
// Apply Response middleware
for _, f := range c.afterResponse {
if err = f(c, response); err != nil {
break
}
}
return response, err
}
// r get request
func (c *cli) r() *request {
return &request{
queryParam: url.Values{},
header: http.Header{},
pathParam: map[string]string{},
formParam: map[string]string{},
fileParam: map[string]string{},
client: c,
}
}
type response struct {
request *request
rawResponse *protocol.Response
bodyByte []byte
size int64
}
// statusCode method returns the HTTP status code for the executed request.
func (r *response) statusCode() int {
if r.rawResponse == nil {
return 0
}
return r.rawResponse.StatusCode()
}
// body method returns HTTP response as []byte array for the executed request.
func (r *response) body() []byte {
if r.rawResponse == nil {
return []byte{}
}
return r.bodyByte
}
// Header method returns the response headers
func (r *response) header() http.Header {
if r.rawResponse == nil {
return http.Header{}
}
h := http.Header{}
r.rawResponse.Header.VisitAll(func(key, value []byte) {
h.Add(string(key), string(value))
})
return h
}
type request struct {
client *cli
url string
method string
queryParam url.Values
header http.Header
pathParam map[string]string
formParam map[string]string
fileParam map[string]string
bodyParam interface{}
rawRequest *protocol.Request
ctx context.Context
requestOptions []config.RequestOption
result interface{}
Error interface{}
}
func (r *request) setContext(ctx context.Context) *request {
r.ctx = ctx
return r
}
func (r *request) context() context.Context {
return r.ctx
}
func (r *request) setHeader(header, value string) *request {
r.header.Set(header, value)
return r
}
func (r *request) addHeader(header, value string) *request {
r.header.Add(header, value)
return r
}
func (r *request) addHeaders(params map[string]string) *request {
for k, v := range params {
r.addHeader(k, v)
}
return r
}
func (r *request) setQueryParam(param string, value interface{}) *request {
v := reflect.ValueOf(value)
switch v.Kind() {
case reflect.Slice, reflect.Array:
for index := 0; index < v.Len(); index++ {
r.queryParam.Add(param, fmt.Sprint(v.Index(index).Interface()))
}
case reflect.Int32, reflect.Int64:
if r.client.enumAsInt {
r.queryParam.Add(param, fmt.Sprintf("%d", v.Interface()))
} else {
r.queryParam.Add(param, fmt.Sprint(v))
}
default:
r.queryParam.Set(param, fmt.Sprint(v))
}
return r
}
func (r *request) setResult(res interface{}) *request {
r.result = res
return r
}
func (r *request) setError(err interface{}) *request {
r.Error = err
return r
}
func (r *request) setHeaders(headers map[string]string) *request {
for h, v := range headers {
r.setHeader(h, v)
}
return r
}
func (r *request) setQueryParams(params map[string]interface{}) *request {
for p, v := range params {
r.setQueryParam(p, v)
}
return r
}
func (r *request) setPathParams(params map[string]string) *request {
for p, v := range params {
r.pathParam[p] = v
}
return r
}
func (r *request) setFormParams(params map[string]string) *request {
for p, v := range params {
r.formParam[p] = v
}
return r
}
func (r *request) setFormFileParams(params map[string]string) *request {
for p, v := range params {
r.fileParam[p] = v
}
return r
}
func (r *request) setBodyParam(body interface{}) *request {
r.bodyParam = body
return r
}
func (r *request) setRequestOption(option ...config.RequestOption) *request {
r.requestOptions = append(r.requestOptions, option...)
return r
}
func (r *request) execute(method, url string) (*response, error) {
r.method = method
r.url = url
return r.client.execute(r)
}
func parseRequestURL(c *cli, r *request) error {
if len(r.pathParam) > 0 {
for p, v := range r.pathParam {
r.url = strings.Replace(r.url, ":"+p, url.PathEscape(v), -1)
}
}
// Parsing request URL
reqURL, err := url.Parse(r.url)
if err != nil {
return err
}
// If request.URL is relative path then added c.HostURL into
// the request URL otherwise request.URL will be used as-is
if !reqURL.IsAbs() {
r.url = reqURL.String()
if len(r.url) > 0 && r.url[0] != '/' {
r.url = "/" + r.url
}
reqURL, err = url.Parse(c.hostUrl + r.url)
if err != nil {
return err
}
}
// Adding Query Param
query := make(url.Values)
for k, v := range r.queryParam {
// remove query param from client level by key
// since overrides happens for that key in the request
query.Del(k)
for _, iv := range v {
query.Add(k, iv)
}
}
if len(query) > 0 {
if isStringEmpty(reqURL.RawQuery) {
reqURL.RawQuery = query.Encode()
} else {
reqURL.RawQuery = reqURL.RawQuery + "&" + query.Encode()
}
}
r.url = reqURL.String()
return nil
}
func isStringEmpty(str string) bool {
return len(strings.TrimSpace(str)) == 0
}
func parseRequestHeader(c *cli, r *request) error {
hdr := make(http.Header)
if c.header != nil {
for k := range c.header {
hdr[k] = append(hdr[k], c.header[k]...)
}
}
for k := range r.header {
hdr.Del(k)
hdr[k] = append(hdr[k], r.header[k]...)
}
if len(r.formParam) != 0 || len(r.fileParam) != 0 {
hdr.Add(hdrContentTypeKey, formContentType)
}
r.header = hdr
return nil
}
// detectContentType method is used to figure out "request.Body" content type for request header
func detectContentType(body interface{}) string {
contentType := plainTextType
kind := reflect.Indirect(reflect.ValueOf(body)).Kind()
switch kind {
case reflect.Struct, reflect.Map:
contentType = jsonContentType
case reflect.String:
contentType = plainTextType
default:
if b, ok := body.([]byte); ok {
contentType = http.DetectContentType(b)
} else if kind == reflect.Slice {
contentType = jsonContentType
}
}
return contentType
}
func defaultRequestBodyBind(c *cli, r *request) (contentType string, body io.Reader, err error) {
if !isPayloadSupported(r.method) {
return
}
var bodyBytes []byte
contentType = r.header.Get(hdrContentTypeKey)
if isStringEmpty(contentType) {
contentType = detectContentType(r.bodyParam)
r.header.Set(hdrContentTypeKey, contentType)
}
kind := reflect.Indirect(reflect.ValueOf(r.bodyParam)).Kind()
if isJSONType(contentType) &&
(kind == reflect.Struct || kind == reflect.Map || kind == reflect.Slice) {
bodyBytes, err = json.Marshal(r.bodyParam)
} else if isXMLType(contentType) && (kind == reflect.Struct) {
bodyBytes, err = xml.Marshal(r.bodyParam)
}
if err != nil {
return
}
return contentType, strings.NewReader(string(bodyBytes)), nil
}
func isPayloadSupported(m string) bool {
return !(m == http.MethodHead || m == http.MethodOptions || m == http.MethodGet || m == http.MethodDelete)
}
func createHTTPRequest(c *cli, r *request) (err error) {
contentType, body, err := c.bindRequestBody(c, r)
if !isStringEmpty(contentType) {
r.header.Set(hdrContentTypeKey, contentType)
}
if err == nil {
r.rawRequest = protocol.NewRequest(r.method, r.url, body)
if contentType == formContentType && isPayloadSupported(r.method) {
if r.rawRequest.IsBodyStream() {
r.rawRequest.ResetBody()
}
r.rawRequest.SetMultipartFormData(r.formParam)
r.rawRequest.SetFiles(r.fileParam)
}
for key, values := range r.header {
for _, val := range values {
r.rawRequest.Header.Add(key, val)
}
}
r.rawRequest.SetOptions(r.requestOptions...)
}
return err
}
func silently(_ ...interface{}) {}
// defaultResponseResultDecider method returns true if HTTP status code >= 400 otherwise false.
func defaultResponseResultDecider(statusCode int, rawResponse *protocol.Response) bool {
return statusCode > 399
}
// IsJSONType method is to check JSON content type or not
func isJSONType(ct string) bool {
return jsonCheck.MatchString(ct)
}
// IsXMLType method is to check XML content type or not
func isXMLType(ct string) bool {
return xmlCheck.MatchString(ct)
}
func parseResponseBody(c *cli, res *response) (err error) {
if res.statusCode() == http.StatusNoContent {
return
}
// Handles only JSON or XML content type
ct := res.header().Get(hdrContentTypeKey)
isError := c.responseResultDecider(res.statusCode(), res.rawResponse)
if isError {
if res.request.Error != nil {
if isJSONType(ct) || isXMLType(ct) {
err = unmarshalContent(ct, res.bodyByte, res.request.Error)
}
} else {
jsonByte, jsonErr := json.Marshal(map[string]interface{}{
"status_code": res.rawResponse.StatusCode(),
"body": string(res.bodyByte),
})
if jsonErr != nil {
return jsonErr
}
err = fmt.Errorf(string(jsonByte))
}
} else if res.request.result != nil {
if isJSONType(ct) || isXMLType(ct) {
err = unmarshalContent(ct, res.bodyByte, res.request.result)
return
}
}
return
}
// unmarshalContent content into object from JSON or XML
func unmarshalContent(ct string, b []byte, d interface{}) (err error) {
if isJSONType(ct) {
err = json.Unmarshal(b, d)
} else if isXMLType(ct) {
err = xml.Unmarshal(b, d)
}
return
}
`
var idlClientTpl = `// Code generated by hertz generator.
package {{.PackageName}}
import (
"context"
"fmt"
"github.com/cloudwego/hertz/pkg/common/config"
"github.com/cloudwego/hertz/pkg/protocol"
{{- range $k, $v := .Imports}}
{{$k}} "{{$v.Package}}"
{{- end}}
)
// unused protection
var (
_ = fmt.Formatter(nil)
)
type Client interface {
{{range $_, $MethodInfo := .ClientMethods}}
{{$MethodInfo.Name}}(context context.Context, req *{{$MethodInfo.RequestTypeName}}, reqOpt ...config.RequestOption) (resp *{{$MethodInfo.ReturnTypeName}}, rawResponse *protocol.Response, err error)
{{end}}
}
type {{.ServiceName}}Client struct {
client *cli
}
func New{{.ServiceName}}Client(hostUrl string, ops ...Option) (Client, error) {
opts := getOptions(append(ops, withHostUrl(hostUrl))...)
cli, err := newClient(opts)
if err != nil {
return nil, err
}
return &{{.ServiceName}}Client{
client: cli,
}, nil
}
{{range $_, $MethodInfo := .ClientMethods}}
func (s *{{$.ServiceName}}Client) {{$MethodInfo.Name}}(context context.Context, req *{{$MethodInfo.RequestTypeName}}, reqOpt ...config.RequestOption) (resp *{{$MethodInfo.ReturnTypeName}}, rawResponse *protocol.Response, err error) {
httpResp := &{{$MethodInfo.ReturnTypeName}}{}
ret, err := s.client.r().
setContext(context).
setQueryParams(map[string]interface{}{
{{$MethodInfo.QueryParamsCode}}
}).
setPathParams(map[string]string{
{{$MethodInfo.PathParamsCode}}
}).
addHeaders(map[string]string{
{{$MethodInfo.HeaderParamsCode}}
}).
setFormParams(map[string]string{
{{$MethodInfo.FormValueCode}}
}).
setFormFileParams(map[string]string{
{{$MethodInfo.FormFileCode}}
}).
{{$MethodInfo.BodyParamsCode}}
setRequestOption(reqOpt...).
setResult(httpResp).
execute("{{if EqualFold $MethodInfo.HTTPMethod "Any"}}POST{{else}}{{ $MethodInfo.HTTPMethod }}{{end}}", "{{$MethodInfo.Path}}")
if err != nil {
return nil, nil, err
}
resp = httpResp
rawResponse = ret.rawResponse
return resp, rawResponse, nil
}
{{end}}
var defaultClient, _ = New{{.ServiceName}}Client("{{.BaseDomain}}")
func ConfigDefaultClient(ops ...Option) (err error) {
defaultClient, err = New{{.ServiceName}}Client("{{.BaseDomain}}", ops...)
return
}
{{range $_, $MethodInfo := .ClientMethods}}
func {{$MethodInfo.Name}}(context context.Context, req *{{$MethodInfo.RequestTypeName}}, reqOpt ...config.RequestOption) (resp *{{$MethodInfo.ReturnTypeName}}, rawResponse *protocol.Response, err error) {
return defaultClient.{{$MethodInfo.Name}}(context, req, reqOpt...)
}
{{end}}
`