github.com/ronaksoft/rony@v0.16.26-0.20230807065236-1743dbfe6959/cmd/protoc-gen-gorony/rpc/gen.go (about) 1 package rpc 2 3 import ( 4 "github.com/ronaksoft/rony" 5 "google.golang.org/protobuf/proto" 6 7 // "github.com/ronaksoft/rony" 8 // "github.com/ronaksoft/rony" 9 "text/template" 10 11 "github.com/ronaksoft/rony/internal/codegen" 12 "google.golang.org/protobuf/compiler/protogen" 13 ) 14 15 /* 16 Creation Time: 2021 - Mar - 02 17 Created by: (ehsan) 18 Maintainers: 19 1. Ehsan N. Moosa (E2) 20 Auditor: Ehsan N. Moosa (E2) 21 Copyright Ronak Software Group 2020 22 */ 23 24 func GenFunc(g *protogen.GeneratedFile, _ *codegen.PluginOptions, files ...*protogen.File) error { 25 for _, f := range files { 26 templateArg := codegen.GenTemplateArg(f) 27 if len(templateArg.Services) > 0 { 28 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/edge"}) 29 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/errors"}) 30 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "google.golang.org/protobuf/proto"}) 31 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "fmt"}) 32 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "context"}) 33 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony"}) 34 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/tools"}) 35 36 g.P("var _ = tools.TimeUnix()") 37 for _, arg := range templateArg.Services { 38 g.P(codegen.ExecTemplate(template.Must(template.New("genServerInterface").Parse(genServerInterface)), arg)) 39 g.P(codegen.ExecTemplate(template.Must(template.New("genServerWrapper").Parse(genServerWrapper)), arg)) 40 g.P(codegen.ExecTemplate(template.Must(template.New("genTunnelCommand").Parse(genTunnelCommand)), arg)) 41 if arg.HasRestProxy { 42 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "net/http"}) 43 g.P(codegen.ExecTemplate(template.Must(template.New("genServerRestProxy").Parse(genServerRestProxy)), arg)) 44 } 45 46 if !proto.GetExtension(arg.Options(), rony.E_RonyNoClient).(bool) { 47 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/edgec"}) 48 g.P(codegen.ExecTemplate(template.Must(template.New("genClientInterface").Parse(genClientInterface)), arg)) 49 g.P(codegen.ExecTemplate(template.Must(template.New("genClient").Parse(genClient)), arg)) 50 } 51 cliOpt := proto.GetExtension(arg.Options(), rony.E_RonyCli).(*rony.CliOpt) 52 if cliOpt != nil { 53 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/spf13/cobra"}) 54 g.QualifiedGoIdent(protogen.GoIdent{GoName: "", GoImportPath: "github.com/ronaksoft/rony/config"}) 55 g.P(codegen.ExecTemplate(template.Must(template.New("genCobraCmd").Parse(genCobraCmd)), arg)) 56 } 57 } 58 } 59 } 60 61 return nil 62 } 63 64 const genServerRestProxy = ` 65 {{- $service := . }} 66 {{- range .Methods }} 67 func (sw *{{$service.NameCC}}Wrapper) {{.NameCC}}RestClient (conn rony.RestConn, ctx *edge.DispatchCtx) error { 68 {{- if eq .Input.Pkg "" }} 69 req := &{{.Input.Name}}{} 70 {{- else }} 71 req := &{{.Input.Pkg}}{{.Input.Name}}{} 72 {{- end }} 73 74 {{- if .Rest.Unmarshal }} 75 {{- if .Rest.Json }} 76 if len(conn.Body()) > 0 { 77 err := req.UnmarshalJSON(conn.Body()) 78 if err != nil { 79 return err 80 } 81 } 82 {{- else }} 83 err := req.Unmarshal(conn.Body()) 84 if err != nil { 85 return err 86 } 87 {{- end }} 88 {{- end }} 89 90 {{- range .Rest.ExtraCode }} 91 {{.}} 92 {{- end }} 93 94 ctx.Fill(conn.ConnID(), C_{{.Fullname}}, req) 95 return nil 96 } 97 func (sw *{{$service.NameCC}}Wrapper) {{.NameCC}}RestServer(conn rony.RestConn, ctx *edge.DispatchCtx) (err error) { 98 {{- if .Rest.Json }} 99 conn.WriteHeader("Content-Type", "application/json") 100 {{- end }} 101 if !ctx.BufferPop(func(envelope *rony.MessageEnvelope) { 102 switch envelope.Constructor { 103 case {{.Output.CName}}: 104 x := &{{.Output.Name}}{} 105 _ = x.Unmarshal(envelope.Message) 106 var b []byte 107 {{- if .Rest.Json }} 108 b, err = x.MarshalJSON() 109 {{- else }} 110 b, err = x.Marshal() 111 {{- end }} 112 if err != nil { 113 return 114 } 115 err = conn.WriteBinary(ctx.StreamID(), b) 116 return 117 case rony.C_Error: 118 x := &rony.Error{} 119 _ = x.Unmarshal(envelope.Message) 120 err = x 121 return 122 case rony.C_Redirect: 123 x := &rony.Redirect{} 124 _ = x.Unmarshal(envelope.Message) 125 if len(x.Edges) == 0 || len(x.Edges[0].HostPorts) == 0 { 126 break 127 } 128 switch x.Reason { 129 case rony.RedirectReason_ReplicaSetSession: 130 conn.Redirect(http.StatusPermanentRedirect, x.Edges[0].HostPorts[0]) 131 case rony.RedirectReason_ReplicaSetRequest: 132 conn.Redirect(http.StatusTemporaryRedirect, x.Edges[0].HostPorts[0]) 133 } 134 return 135 } 136 err = errors.ErrUnexpectedResponse 137 }) { 138 err = errors.ErrInternalServer 139 } 140 141 return 142 } 143 {{ end }} 144 ` 145 146 const genServerInterface = ` 147 type I{{.Name}} interface { 148 {{- range .Methods }} 149 {{.Name}} (ctx *edge.RequestCtx, req *{{.Input.Fullname}}, res *{{.Output.Fullname}}) *rony.Error 150 {{- end }} 151 } 152 153 func Register{{.Name}} (h I{{.Name}}, e *edge.Server, preHandlers ...edge.Handler) { 154 w := {{.NameCC}}Wrapper { 155 h: h, 156 } 157 w.Register(e, func(c uint64) []edge.Handler { 158 return preHandlers 159 }) 160 } 161 162 func Register{{.Name}}WithFunc(h I{{.Name}}, e *edge.Server, handlerFunc func(c uint64) []edge.Handler) { 163 w := {{.NameCC}}Wrapper { 164 h: h, 165 } 166 w.Register(e, handlerFunc) 167 } 168 ` 169 170 const genServerWrapper = ` 171 type {{.NameCC}}Wrapper struct { 172 h I{{.Name}} 173 } 174 175 {{- $serviceNameCC := .NameCC -}} 176 {{- $serviceName := .Name -}} 177 {{- range .Methods }} 178 func (sw *{{$serviceNameCC}}Wrapper) {{.NameCC}}Wrapper(ctx *edge.RequestCtx, in *rony.MessageEnvelope) { 179 {{- if eq .Input.Pkg "" }} 180 req := &{{.Input.Name}}{} 181 {{- else }} 182 req := &{{.Input.Pkg}}.{{.Input.Name}}{} 183 {{- end }} 184 {{- if eq .Output.Pkg "" }} 185 res := &{{.Output.Name}}{} 186 {{- else }} 187 res := &{{.Output.Pkg}}.{{.Output.Name}}{} 188 {{- end }} 189 190 var err error 191 if in.JsonEncoded { 192 err = protojson.Unmarshal(in.Message, req) 193 } else { 194 err = proto.UnmarshalOptions{Merge:true}.Unmarshal(in.Message, req) 195 } 196 if err != nil { 197 ctx.PushError(errors.ErrInvalidRequest) 198 return 199 } 200 201 rErr := sw.h.{{.Name}} (ctx, req, res) 202 if rErr != nil { 203 ctx.PushError(rErr) 204 return 205 } 206 if !ctx.Stopped() { 207 {{- if eq .Output.Pkg "" }} 208 ctx.PushMessage(C_{{.Output.Name}}, res) 209 {{- else }} 210 ctx.PushMessage({{.Output.Pkg}}.C_{{.Output.Name}}, res) 211 {{- end }} 212 } 213 } 214 {{- end }} 215 216 func (sw *{{.NameCC}}Wrapper) Register (e *edge.Server, handlerFunc func(c uint64) []edge.Handler) { 217 if handlerFunc == nil { 218 handlerFunc = func(c uint64) []edge.Handler { 219 return nil 220 } 221 } 222 223 {{- range .Methods }} 224 e.SetHandler( 225 edge.NewHandlerOptions(). 226 SetConstructor(C_{{.Fullname}}). 227 SetServiceName("{{$serviceName}}"). 228 SetMethodName("{{.Name}}"). 229 SetHandler(handlerFunc(C_{{.Fullname}})...). 230 Append(sw.{{.NameCC}}Wrapper) 231 {{- if .TunnelOnly }}.TunnelOnly(){{- end }}, 232 ) 233 {{- if .RestEnabled }} 234 e.SetRestProxy( 235 "{{.Rest.Method}}", "{{.Rest.Path}}", 236 edge.NewRestProxy(sw.{{.NameCC}}RestClient, sw.{{.NameCC}}RestServer), 237 ) 238 {{- end }} 239 {{- end }} 240 } 241 ` 242 243 const genClient = ` 244 type {{.Name}}Client struct { 245 name string 246 c edgec.Client 247 } 248 249 func New{{.Name}}Client(name string, ec edgec.Client) *{{.Name}}Client { 250 return &{{.Name}}Client{ 251 name: name, 252 c: ec, 253 } 254 } 255 256 {{- $serviceName := .Name -}} 257 {{- range .Methods }} 258 {{- if not .TunnelOnly }} 259 func (c *{{$serviceName}}Client) {{.Name}} ( 260 ctx context.Context, req *{{.Input.Fullname}}, kvs ...*rony.KeyValue, 261 ) (*{{.Output.Fullname}}, error) { 262 out := rony.PoolMessageEnvelope.Get() 263 defer rony.PoolMessageEnvelope.Put(out) 264 in := rony.PoolMessageEnvelope.Get() 265 defer rony.PoolMessageEnvelope.Put(in) 266 out.Fill(c.c.GetRequestID(), C_{{.Fullname}}, req, kvs ...) 267 err := c.c.Send(ctx, out, in) 268 if err != nil { 269 return nil, err 270 } 271 switch in.GetConstructor() { 272 {{- if eq .Output.Pkg "" }} 273 case C_{{.Output.Name}}: 274 {{- else }} 275 case {{.Output.Pkg}}.C_{{.Output.Name}}: 276 {{- end }} 277 x := &{{.Output.Name}}{} 278 _ = proto.Unmarshal(in.Message, x) 279 return x, nil 280 case rony.C_Error: 281 x := &rony.Error{} 282 _ = x.Unmarshal(in.Message) 283 return nil, x 284 default: 285 return nil, fmt.Errorf("unknown message :%d", in.GetConstructor()) 286 } 287 } 288 {{- end }} 289 {{- end }} 290 ` 291 292 const genClientInterface = ` 293 type I{{.Name}}Client interface { 294 {{- $serviceName := .Name -}} 295 {{- range .Methods }} 296 {{- if not .TunnelOnly }} 297 {{.Name}} (ctx context.Context, req *{{.Input.Fullname}}, kvs ...*rony.KeyValue) (*{{.Output.Fullname}}, error) 298 {{- end }} 299 {{- end }} 300 } 301 ` 302 303 const genTunnelCommand = ` 304 {{- $serviceName := .Name -}} 305 {{- range .Methods }} 306 func TunnelRequest{{$serviceName}}{{.Name}} ( 307 ctx *edge.RequestCtx, replicaSet uint64, 308 req *{{.Input.Fullname}}, res *{{.Output.Fullname}}, 309 kvs ...*rony.KeyValue, 310 ) error { 311 out := rony.PoolMessageEnvelope.Get() 312 defer rony.PoolMessageEnvelope.Put(out) 313 in := rony.PoolMessageEnvelope.Get() 314 defer rony.PoolMessageEnvelope.Put(in) 315 out.Fill(ctx.ReqID(), C_{{.Fullname}}, req, kvs...) 316 err := ctx.TunnelRequest(replicaSet, out, in) 317 if err != nil { 318 return err 319 } 320 321 switch in.GetConstructor() { 322 case {{.Output.CName}}: 323 _ = res.Unmarshal(in.GetMessage()) 324 return nil 325 case rony.C_Error: 326 x := &rony.Error{} 327 _ = x.Unmarshal(in.GetMessage()) 328 return x 329 default: 330 return errors.ErrUnexpectedTunnelResponse 331 } 332 } 333 {{- end }} 334 ` 335 336 const genCobraCmd = ` 337 func prepare{{.Name}}Command(cmd *cobra.Command, c edgec.Client) (*{{.Name}}Client, error) { 338 // Bind current flags to the registered flags in config package 339 err := config.BindCmdFlags(cmd) 340 if err != nil { 341 return nil, err 342 } 343 344 return New{{.Name}}Client("{{.Name}}",c), nil 345 } 346 347 {{- $serviceName := .Name -}} 348 {{- range .Methods }} 349 {{- if not .TunnelOnly }} 350 var gen{{$serviceName}}{{.Name}}Cmd = func(h I{{$serviceName}}Cli, c edgec.Client) *cobra.Command { 351 cmd := &cobra.Command { 352 Use: "{{.NameKC}}", 353 RunE: func(cmd *cobra.Command, args []string) error { 354 cli, err := prepare{{$serviceName}}Command(cmd, c) 355 if err != nil { 356 return err 357 } 358 return h.{{.Name}}(cli, cmd, args) 359 }, 360 } 361 config.SetFlags(cmd, 362 {{- range .Input.Fields }} 363 {{- if or (eq .GoKind "string") (eq .GoKind "[]byte") }} 364 config.StringFlag("{{.NameCC}}", "{{.DefaultValue}}", "{{.HelpText}}"), 365 {{- else if eq .GoKind "int64" }} 366 config.Int64Flag("{{.NameCC}}", tools.StrToInt64("{{.DefaultValue}}"), "{{.HelpText}}"), 367 {{- else if eq .GoKind "uint64" }} 368 config.Uint64Flag("{{.NameCC}}", tools.StrToUInt64("{{.DefaultValue}}"), "{{.HelpText}}"), 369 {{- else if eq .GoKind "int32" }} 370 config.Int32Flag("{{.NameCC}}", tools.StrToInt32("{{.DefaultValue}}"), "{{.HelpText}}"), 371 {{- else if eq .GoKind "uint32" }} 372 config.Uint32Flag("{{.NameCC}}", tools.StrToUInt32("{{.DefaultValue}}"), "{{.HelpText}}"), 373 {{- else if eq .GoKind "bool" }} 374 config.BoolFlag("{{.NameCC}}", false, "{{.HelpText}}"), 375 {{- end }} 376 {{- end }} 377 ) 378 return cmd 379 } 380 {{- end }} 381 {{- end }} 382 383 type I{{.Name}}Cli interface { 384 {{- range .Methods }} 385 {{- if not .TunnelOnly }} 386 {{.Name}} (cli *{{$serviceName}}Client, cmd *cobra.Command, args []string) error 387 {{- end }} 388 {{- end }} 389 } 390 391 func Register{{$serviceName}}Cli (h I{{$serviceName}}Cli, c edgec.Client, rootCmd *cobra.Command) { 392 subCommand := &cobra.Command{ 393 Use: "{{$serviceName}}", 394 } 395 subCommand.AddCommand( 396 {{- range .Methods }} 397 {{- if not .TunnelOnly }} 398 gen{{$serviceName}}{{.Name}}Cmd(h ,c), 399 {{- end }} 400 {{- end }} 401 ) 402 403 rootCmd.AddCommand(subCommand) 404 } 405 `