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  `