979 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			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}}
 | |
| `
 |