github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/cmd/internal/svc/codegen/httpclient.go (about) 1 package codegen 2 3 import ( 4 "bufio" 5 "bytes" 6 "github.com/iancoleman/strcase" 7 "github.com/sirupsen/logrus" 8 "github.com/unionj-cloud/go-doudou/cmd/internal/astutils" 9 v3helper "github.com/unionj-cloud/go-doudou/cmd/internal/openapi/v3" 10 "github.com/unionj-cloud/go-doudou/toolkit/copier" 11 "github.com/unionj-cloud/go-doudou/version" 12 "os" 13 "path/filepath" 14 "strings" 15 "text/template" 16 ) 17 18 var clientTmpl = `/** 19 * Generated by go-doudou {{.Version}}. 20 * Don't edit! 21 */ 22 package client 23 24 import ( 25 "context" 26 "encoding/json" 27 "github.com/go-resty/resty/v2" 28 "github.com/pkg/errors" 29 "github.com/unionj-cloud/go-doudou/toolkit/fileutils" 30 "github.com/unionj-cloud/go-doudou/toolkit/stringutils" 31 "github.com/unionj-cloud/go-doudou/framework/registry" 32 ddhttp "github.com/unionj-cloud/go-doudou/framework/http" 33 v3 "github.com/unionj-cloud/go-doudou/toolkit/openapi/v3" 34 "github.com/opentracing-contrib/go-stdlib/nethttp" 35 "github.com/opentracing/opentracing-go" 36 "io" 37 "net/http" 38 "mime/multipart" 39 "net/url" 40 "os" 41 "path/filepath" 42 "strings" 43 "{{.VoPackage}}" 44 ) 45 46 type {{.Meta.Name}}Client struct { 47 provider registry.IServiceProvider 48 client *resty.Client 49 rootPath string 50 } 51 52 func (receiver *{{.Meta.Name}}Client) SetRootPath(rootPath string) { 53 receiver.rootPath = rootPath 54 } 55 56 func (receiver *{{.Meta.Name}}Client) SetProvider(provider registry.IServiceProvider) { 57 receiver.provider = provider 58 } 59 60 func (receiver *{{.Meta.Name}}Client) SetClient(client *resty.Client) { 61 receiver.client = client 62 } 63 64 {{- range $m := .Meta.Methods }} 65 func (receiver *{{$.Meta.Name}}Client) {{$m.Name}}(ctx context.Context, _headers map[string]string, {{- range $i, $p := $m.Params}} 66 {{- if ne $p.Type "context.Context" }} 67 {{- $p.Name}} {{$p.Type}}, 68 {{- end }} 69 {{- end }}) (_resp *resty.Response, {{- range $i, $r := $m.Results}} 70 {{- if $i}},{{end}} 71 {{- $r.Name}} {{$r.Type}} 72 {{- end }}) { 73 var _err error 74 _urlValues := url.Values{} 75 _req := receiver.client.R() 76 if len(_headers) > 0 { 77 _req.SetHeaders(_headers) 78 } 79 _req.SetContext(ctx) 80 {{- range $p := $m.Params }} 81 {{- if or (eq $p.Type "*multipart.FileHeader") (eq $p.Type "[]*multipart.FileHeader") }} 82 {{- if contains $p.Type "["}} 83 for _, _fh := range {{$p.Name}} { 84 _f, _err := _fh.Open() 85 if _err != nil { 86 {{- range $r := $m.Results }} 87 {{- if eq $r.Type "error" }} 88 {{ $r.Name }} = errors.Wrap(_err, "error") 89 {{- end }} 90 {{- end }} 91 return 92 } 93 _req.SetFileReader("{{$p.Name}}", _fh.Filename, _f) 94 } 95 {{- else}} 96 if _f, _err := {{$p.Name}}.Open(); _err != nil { 97 {{- range $r := $m.Results }} 98 {{- if eq $r.Type "error" }} 99 {{ $r.Name }} = errors.Wrap(_err, "error") 100 {{- end }} 101 {{- end }} 102 return 103 } else { 104 _req.SetFileReader("{{$p.Name}}", {{$p.Name}}.Filename, _f) 105 } 106 {{- end}} 107 {{- else if or (eq $p.Type "v3.FileModel") (eq $p.Type "*v3.FileModel") (eq $p.Type "[]v3.FileModel") (eq $p.Type "*[]v3.FileModel") (eq $p.Type "...v3.FileModel") }} 108 {{- if isSlice $p.Type }} 109 {{- if isOptional $p.Type }} 110 if {{$p.Name}} != nil { 111 {{- if isVarargs $p.Type }} 112 for _, _f := range {{$p.Name}} { 113 {{- else }} 114 for _, _f := range *{{$p.Name}} { 115 {{- end }} 116 _req.SetFileReader("{{$p.Name}}", _f.Filename, _f.Reader) 117 } 118 } 119 {{- else }} 120 if len({{$p.Name}}) == 0 { 121 {{- range $r := $m.Results }} 122 {{- if eq $r.Type "error" }} 123 {{ $r.Name }} = errors.New("at least one file should be uploaded for parameter {{$p.Name}}") 124 {{- end }} 125 {{- end }} 126 return 127 } 128 for _, _f := range {{$p.Name}} { 129 _req.SetFileReader("{{$p.Name}}", _f.Filename, _f.Reader) 130 } 131 {{- end }} 132 {{- else }} 133 {{- if isOptional $p.Type }} 134 if {{$p.Name}} != nil { 135 _req.SetFileReader("{{$p.Name}}", {{$p.Name}}.Filename, {{$p.Name}}.Reader) 136 } 137 {{- else }} 138 _req.SetFileReader("{{$p.Name}}", {{$p.Name}}.Filename, {{$p.Name}}.Reader) 139 {{- end }} 140 {{- end }} 141 {{- else if eq $p.Type "context.Context" }} 142 {{- else if not (isBuiltin $p)}} 143 _req.SetBody({{$p.Name}}) 144 {{- else if isSlice $p.Type }} 145 {{- if isOptional $p.Type }} 146 if {{$p.Name}} != nil { 147 {{- if isVarargs $p.Type }} 148 for _, _item := range {{$p.Name}} { 149 {{- else }} 150 for _, _item := range *{{$p.Name}} { 151 {{- end }} 152 {{- if IsEnum $p }} 153 _urlValues.Add("{{$p.Name}}", _item.StringGetter()) 154 {{- else }} 155 _urlValues.Add("{{$p.Name}}", fmt.Sprintf("%v", _item)) 156 {{- end }} 157 } 158 } 159 {{- else }} 160 if len({{$p.Name}}) == 0 { 161 {{- range $r := $m.Results }} 162 {{- if eq $r.Type "error" }} 163 {{ $r.Name }} = errors.New("size of parameter {{$p.Name}} should be greater than zero") 164 {{- end }} 165 {{- end }} 166 return 167 } 168 for _, _item := range {{$p.Name}} { 169 {{- if IsEnum $p }} 170 _urlValues.Add("{{$p.Name}}", _item.StringGetter()) 171 {{- else }} 172 _urlValues.Add("{{$p.Name}}", fmt.Sprintf("%v", _item)) 173 {{- end }} 174 } 175 {{- end}} 176 {{- else }} 177 {{- if isOptional $p.Type }} 178 if {{$p.Name}} != nil { 179 {{- if IsEnum $p }} 180 _urlValues.Set("{{$p.Name}}", {{$p.Name}}.StringGetter()) 181 {{- else }} 182 _urlValues.Set("{{$p.Name}}", fmt.Sprintf("%v", *{{$p.Name}})) 183 {{- end }} 184 } 185 {{- else }} 186 {{- if IsEnum $p }} 187 _urlValues.Set("{{$p.Name}}", {{$p.Name}}.StringGetter()) 188 {{- else }} 189 _urlValues.Set("{{$p.Name}}", fmt.Sprintf("%v", {{$p.Name}})) 190 {{- end }} 191 {{- end }} 192 {{- end }} 193 {{- end }} 194 195 {{- range $r := $m.Results }} 196 {{- if eq $r.Type "*os.File" }} 197 _req.SetDoNotParseResponse(true) 198 {{- end }} 199 {{- end }} 200 201 {{- if eq $.RoutePatternStrategy 1}} 202 _path := "/{{$.Meta.Name | lower}}/{{$m.Name | noSplitPattern}}" 203 {{- else }} 204 _path := "/{{$m.Name | pattern}}" 205 {{- end }} 206 207 {{- if eq ($m.Name | httpMethod) "GET" }} 208 _resp, _err = _req.SetQueryParamsFromValues(_urlValues). 209 Get(_path) 210 {{- else }} 211 if _req.Body != nil { 212 _req.SetQueryParamsFromValues(_urlValues) 213 } else { 214 _req.SetFormDataFromValues(_urlValues) 215 } 216 _resp, _err = _req.{{$m.Name | restyMethod}}(_path) 217 {{- end }} 218 if _err != nil { 219 {{- range $r := $m.Results }} 220 {{- if eq $r.Type "error" }} 221 {{ $r.Name }} = errors.Wrap(_err, "error") 222 {{- end }} 223 {{- end }} 224 return 225 } 226 if _resp.IsError() { 227 {{- range $r := $m.Results }} 228 {{- if eq $r.Type "error" }} 229 {{ $r.Name }} = errors.New(_resp.String()) 230 {{- end }} 231 {{- end }} 232 return 233 } 234 {{- $done := false }} 235 {{- range $r := $m.Results }} 236 {{- if eq $r.Type "*os.File" }} 237 _disp := _resp.Header().Get("Content-Disposition") 238 _file := strings.TrimPrefix(_disp, "attachment; filename=") 239 _output := os.TempDir() 240 if stringutils.IsNotEmpty(_output) { 241 _file = _output + string(filepath.Separator) + _file 242 } 243 _file = filepath.Clean(_file) 244 if _err = fileutils.CreateDirectory(filepath.Dir(_file)); _err != nil { 245 {{- range $r := $m.Results }} 246 {{- if eq $r.Type "error" }} 247 {{ $r.Name }} = errors.Wrap(_err, "error") 248 {{- end }} 249 {{- end }} 250 return 251 } 252 _outFile, _err := os.Create(_file) 253 if _err != nil { 254 {{- range $r := $m.Results }} 255 {{- if eq $r.Type "error" }} 256 {{ $r.Name }} = errors.Wrap(_err, "error") 257 {{- end }} 258 {{- end }} 259 return 260 } 261 defer _outFile.Close() 262 defer _resp.RawBody().Close() 263 _, _err = io.Copy(_outFile, _resp.RawBody()) 264 if _err != nil { 265 {{- range $r := $m.Results }} 266 {{- if eq $r.Type "error" }} 267 {{ $r.Name }} = errors.Wrap(_err, "error") 268 {{- end }} 269 {{- end }} 270 return 271 } 272 {{ $r.Name }} = _outFile 273 return 274 {{- $done = true }} 275 {{- end }} 276 {{- end }} 277 {{- if not $done }} 278 var _result struct { 279 {{- range $r := $m.Results }} 280 {{- if ne $r.Type "error" }} 281 {{ $r.Name | toCamel }} {{ $r.Type }} ` + "`" + `json:"{{ $r.Name | convertCase }}"` + "`" + ` 282 {{- end }} 283 {{- end }} 284 } 285 if _err = json.Unmarshal(_resp.Body(), &_result); _err != nil { 286 {{- range $r := $m.Results }} 287 {{- if eq $r.Type "error" }} 288 {{ $r.Name }} = errors.Wrap(_err, "error") 289 {{- end }} 290 {{- end }} 291 return 292 } 293 return _resp, {{range $i, $r := $m.Results }}{{- if $i}},{{end}}{{ if eq $r.Type "error" }}nil{{else}}_result.{{ $r.Name | toCamel }}{{end}}{{- end }} 294 {{- end }} 295 } 296 {{- end }} 297 298 func New{{.Meta.Name}}Client(opts ...ddhttp.DdClientOption) *{{.Meta.Name}}Client { 299 {{- if .Env }} 300 defaultProvider := ddhttp.NewServiceProvider("{{.Env}}") 301 {{- else }} 302 defaultProvider := ddhttp.NewServiceProvider("{{.Meta.Name | toUpper}}") 303 {{- end }} 304 defaultClient := ddhttp.NewClient() 305 306 svcClient := &{{.Meta.Name}}Client{ 307 provider: defaultProvider, 308 client: defaultClient, 309 } 310 311 for _, opt := range opts { 312 opt(svcClient) 313 } 314 315 svcClient.client.OnBeforeRequest(func(_ *resty.Client, request *resty.Request) error { 316 request.URL = svcClient.provider.SelectServer() + svcClient.rootPath + request.URL 317 return nil 318 }) 319 320 svcClient.client.SetPreRequestHook(func(_ *resty.Client, request *http.Request) error { 321 traceReq, _ := nethttp.TraceRequest(opentracing.GlobalTracer(), request, 322 nethttp.OperationName(fmt.Sprintf("HTTP %s: %s", request.Method, request.URL.Path))) 323 *request = *traceReq 324 return nil 325 }) 326 327 svcClient.client.OnAfterResponse(func(_ *resty.Client, response *resty.Response) error { 328 nethttp.TracerFromRequest(response.Request.RawRequest).Finish() 329 return nil 330 }) 331 332 return svcClient 333 } 334 ` 335 336 func restyMethod(method string) string { 337 return strings.Title(strings.ToLower(httpMethod(method))) 338 } 339 340 // GenGoClient generates golang http client code from result of parsing svc.go file in project root path 341 func GenGoClient(dir string, ic astutils.InterfaceCollector, env string, routePatternStrategy int, caseconvertor func(string) string) { 342 var ( 343 err error 344 clientfile string 345 f *os.File 346 tpl *template.Template 347 sqlBuf bytes.Buffer 348 clientDir string 349 fi os.FileInfo 350 source string 351 modfile string 352 modName string 353 firstLine string 354 modf *os.File 355 meta astutils.InterfaceMeta 356 ) 357 clientDir = filepath.Join(dir, "client") 358 if err = MkdirAll(clientDir, os.ModePerm); err != nil { 359 panic(err) 360 } 361 362 clientfile = filepath.Join(clientDir, "client.go") 363 fi, err = Stat(clientfile) 364 if err != nil && !os.IsNotExist(err) { 365 panic(err) 366 } 367 if fi != nil { 368 logrus.Warningln("file client.go will be overwritten") 369 } 370 if f, err = Create(clientfile); err != nil { 371 panic(err) 372 } 373 defer f.Close() 374 375 _ = copier.DeepCopy(ic.Interfaces[0], &meta) 376 377 modfile = filepath.Join(dir, "go.mod") 378 if modf, err = Open(modfile); err != nil { 379 panic(err) 380 } 381 reader := bufio.NewReader(modf) 382 firstLine, _ = reader.ReadString('\n') 383 modName = strings.TrimSpace(strings.TrimPrefix(firstLine, "module")) 384 385 funcMap := make(map[string]interface{}) 386 funcMap["toLowerCamel"] = strcase.ToLowerCamel 387 funcMap["toCamel"] = strcase.ToCamel 388 funcMap["httpMethod"] = httpMethod 389 funcMap["pattern"] = pattern 390 funcMap["lower"] = strings.ToLower 391 funcMap["contains"] = strings.Contains 392 funcMap["isBuiltin"] = v3helper.IsBuiltin 393 funcMap["restyMethod"] = restyMethod 394 funcMap["toUpper"] = strings.ToUpper 395 funcMap["noSplitPattern"] = noSplitPattern 396 funcMap["isOptional"] = v3helper.IsOptional 397 funcMap["convertCase"] = caseconvertor 398 funcMap["isSlice"] = v3helper.IsSlice 399 funcMap["isVarargs"] = v3helper.IsVarargs 400 funcMap["IsEnum"] = v3helper.IsEnum 401 if tpl, err = template.New("client.go.tmpl").Funcs(funcMap).Parse(clientTmpl); err != nil { 402 panic(err) 403 } 404 if err = tpl.Execute(&sqlBuf, struct { 405 VoPackage string 406 Meta astutils.InterfaceMeta 407 Env string 408 RoutePatternStrategy int 409 Version string 410 }{ 411 VoPackage: modName + "/vo", 412 Meta: meta, 413 Env: env, 414 RoutePatternStrategy: routePatternStrategy, 415 Version: version.Release, 416 }); err != nil { 417 panic(err) 418 } 419 source = strings.TrimSpace(sqlBuf.String()) 420 astutils.FixImport([]byte(source), clientfile) 421 }