From d244d82c85191f958f1cf72928be30ff1cac0ddb Mon Sep 17 00:00:00 2001 From: franklin Date: Tue, 13 Dec 2016 16:38:00 +0100 Subject: [PATCH 01/23] added go tsrpc client generation --- client.go | 46 ++++++++++++++++++++++++++++++++++++++ go.go | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ typereader.go | 4 +++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 client.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..ff4f83d --- /dev/null +++ b/client.go @@ -0,0 +1,46 @@ +package gotsrpc + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "strings" +) + +// CallClient calls a method on the remove service +func CallClient(url string, endpoint string, method string, args []interface{}, reply []interface{}) error { + // Marshall args + jsonArgs := make([]string, len(args)) + for _, value := range args { + jsonArg, err := json.Marshal(value) + if err != nil { + return err + } + jsonArgs = append(jsonArgs, string(jsonArg)) + } + // Create request + request := "[" + strings.Join(jsonArgs, ",") + "]" + // Create post url + postURL := fmt.Sprintf("%s%s%s", url, endpoint, method) + // Post + resp, err := http.Post(postURL, "application/json", strings.NewReader(request)) + if err != nil { + return err + } + defer resp.Body.Close() + // Check status + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Error: %s", resp.Status) + } + // Read in body + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + // Unmarshal reply + if err := json.Unmarshal(body, &reply); err != nil { + return err + } + return nil +} diff --git a/go.go b/go.go index 8180225..047922e 100644 --- a/go.go +++ b/go.go @@ -3,6 +3,8 @@ package gotsrpc import ( "fmt" "strings" + + "github.com/davecgh/go-spew/spew" ) func (v *Value) isHTTPResponseWriter() bool { @@ -50,10 +52,24 @@ func (v *Value) emptyLiteral(aliases map[string]string) (e string) { return "float64(0.0)" case "int": return "int(0)" + case "int8": + return "int8(0)" + case "int16": + return "int16(0)" case "int32": return "int32(0)" case "int64": return "int64(0)" + case "uint": + return "uint(0)" + case "uint8": + return "uint8(0)" + case "uint16": + return "uint16(0)" + case "uint32": + return "uint32(0)" + case "uint64": + return "uint64(0)" case "bool": return "false" } @@ -139,6 +155,7 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, for _, s := range services { for _, m := range s.Methods { extractImports(m.Args) + extractImports(m.Return) } } @@ -147,6 +164,9 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, imports += alias + " \"" + packageName + "\"\n" } + spew.Dump(aliases) + spew.Dump(fullPackageName) + g.l(` // this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc package ` + packageName + ` @@ -295,6 +315,48 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, g.ind(-2).l("}") // close switch g.ind(-1).l("}") // close ServeHttp + clientName := service.Name + "GoTSRPCClient" + g.l(` + type ` + clientName + ` struct { + URL string + EndPoint string + } + + func NewDefault` + clientName + `(url string) *` + clientName + ` { + return New` + clientName + `(url, "` + endpoint + `") + } + + func New` + clientName + `(url string, endpoint string) *` + clientName + ` { + return &` + clientName + `{ + URL: url, + EndPoint: endpoint, + } + } + `) + for _, method := range service.Methods { + args := []string{} + params := []string{} + for _, a := range method.Args { + args = append(args, a.Name) + params = append(params, a.Name+" "+a.Value.goType(aliases, fullPackageName)) + } + rets := []string{} + returns := []string{} + for _, r := range method.Return { + rets = append(rets, "&"+r.Name) + returns = append(returns, r.Name+" "+r.Value.goType(aliases, fullPackageName)) + } + returns = append(returns, "err error") + g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) + g.ind(1) + g.l(`args := []interface{}{` + strings.Join(args, ", ") + `}`) + g.l(`reply := []interface{}{` + strings.Join(rets, ", ") + `}`) + g.l(`err = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) + g.l(`return`) + g.ind(-1) + g.l(`}`) + g.nl() + } } return nil } diff --git a/typereader.go b/typereader.go index 896d7a3..d33e622 100644 --- a/typereader.go +++ b/typereader.go @@ -92,7 +92,9 @@ func getScalarFromAstIdent(ident *ast.Ident) ScalarType { return ScalarTypeString case "bool": return ScalarTypeBool - case "int", "int32", "int64", "float", "float32", "float64": + case "float", "float32", "float64", + "int", "int8", "int16", "int32", "int64", + "uint", "uint8", "uint16", "uint32", "uint64": return ScalarTypeNumber default: if ident.Obj != nil && ident.Obj.Decl != nil && reflect.ValueOf(ident.Obj.Decl).Type().String() == "*ast.TypeSpec" { From 68dc67cac13814b371f9f601a4ba4bcae40dd8ba Mon Sep 17 00:00:00 2001 From: franklin Date: Tue, 13 Dec 2016 17:03:56 +0100 Subject: [PATCH 02/23] fixed unnamed return values --- client.go | 2 +- go.go | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/client.go b/client.go index ff4f83d..166b88b 100644 --- a/client.go +++ b/client.go @@ -22,7 +22,7 @@ func CallClient(url string, endpoint string, method string, args []interface{}, // Create request request := "[" + strings.Join(jsonArgs, ",") + "]" // Create post url - postURL := fmt.Sprintf("%s%s%s", url, endpoint, method) + postURL := fmt.Sprintf("%s%s/%s", url, endpoint, method) // Post resp, err := http.Post(postURL, "application/json", strings.NewReader(request)) if err != nil { diff --git a/go.go b/go.go index 047922e..6045a1d 100644 --- a/go.go +++ b/go.go @@ -3,8 +3,6 @@ package gotsrpc import ( "fmt" "strings" - - "github.com/davecgh/go-spew/spew" ) func (v *Value) isHTTPResponseWriter() bool { @@ -164,9 +162,6 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, imports += alias + " \"" + packageName + "\"\n" } - spew.Dump(aliases) - spew.Dump(fullPackageName) - g.l(` // this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc package ` + packageName + ` @@ -342,9 +337,13 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, } rets := []string{} returns := []string{} - for _, r := range method.Return { - rets = append(rets, "&"+r.Name) - returns = append(returns, r.Name+" "+r.Value.goType(aliases, fullPackageName)) + for i, r := range method.Return { + name := r.Name + if len(name) == 0 { + name = fmt.Sprintf("ret%s_%d", method.Name, i) + } + rets = append(rets, "&"+name) + returns = append(returns, name+" "+r.Value.goType(aliases, fullPackageName)) } returns = append(returns, "err error") g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) From 3be8701fd9f29fa0e8985521e9916e1f349ae78c Mon Sep 17 00:00:00 2001 From: franklin Date: Tue, 13 Dec 2016 17:45:11 +0100 Subject: [PATCH 03/23] fix go scalar args conversion --- client.go | 10 +++++----- go.go | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/client.go b/client.go index 166b88b..df4a22b 100644 --- a/client.go +++ b/client.go @@ -11,7 +11,7 @@ import ( // CallClient calls a method on the remove service func CallClient(url string, endpoint string, method string, args []interface{}, reply []interface{}) error { // Marshall args - jsonArgs := make([]string, len(args)) + jsonArgs := []string{} for _, value := range args { jsonArg, err := json.Marshal(value) if err != nil { @@ -29,15 +29,15 @@ func CallClient(url string, endpoint string, method string, args []interface{}, return err } defer resp.Body.Close() - // Check status - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("Error: %s", resp.Status) - } // Read in body body, err := ioutil.ReadAll(resp.Body) if err != nil { return err } + // Check status + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("%s: %s", resp.Status, string(body)) + } // Unmarshal reply if err := json.Unmarshal(body, &reply); err != nil { return err diff --git a/go.go b/go.go index 6045a1d..f05c377 100644 --- a/go.go +++ b/go.go @@ -259,7 +259,8 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, args = append(args, arg.Value.emptyLiteral(aliases)) switch arg.Value.GoScalarType { - case "int64": + case "int", "int8", "int16", "int32", "int64", + "uint", "uint8", "uint16", "uint32", "uint64": callArgs = append(callArgs, fmt.Sprint(arg.Value.GoScalarType+"(args[", skipArgI, "].(float64))")) default: // assert From 2308da7529a921877d9561bee02d289024582793 Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 13:44:16 +0100 Subject: [PATCH 04/23] added configurable transport --- client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/client.go b/client.go index df4a22b..b06dced 100644 --- a/client.go +++ b/client.go @@ -8,6 +8,9 @@ import ( "strings" ) +// ClientTransport to use for calls +var ClientTransport = &http.Transport{} + // CallClient calls a method on the remove service func CallClient(url string, endpoint string, method string, args []interface{}, reply []interface{}) error { // Marshall args @@ -24,7 +27,8 @@ func CallClient(url string, endpoint string, method string, args []interface{}, // Create post url postURL := fmt.Sprintf("%s%s/%s", url, endpoint, method) // Post - resp, err := http.Post(postURL, "application/json", strings.NewReader(request)) + client := &http.Client{Transport: ClientTransport} + resp, err := client.Post(postURL, "application/json", strings.NewReader(request)) if err != nil { return err } From 1b265a66213f37f3918d48cf6f6fafe1b849bb9b Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 15:51:50 +0100 Subject: [PATCH 05/23] added make demo --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 1a49706..027f1d3 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,8 @@ +.PHONY: demo +demo: + cd demo && gotsrpc -skipgotsrpc config.yml + +.PHONY: install install: GOBIN=/usr/local/bin go install cmd/gotsrpc/gotsrpc.go \ No newline at end of file From 34b65994d3ddfca91da9d4de7bae0c6bec0405fd Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 15:52:21 +0100 Subject: [PATCH 06/23] updated demo --- demo/complex.go | 16 +++--- demo/config.yml | 2 +- demo/gotsrpc.go | 132 +++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 122 insertions(+), 28 deletions(-) diff --git a/demo/complex.go b/demo/complex.go index b951432..278593f 100644 --- a/demo/complex.go +++ b/demo/complex.go @@ -1,8 +1,6 @@ package demo -import "github.com/foomo/gotsrpc/demo/nested" - -//import nstd "github.com/foomo/gotsrpc/demo/nested" +import nstd "github.com/foomo/gotsrpc/demo/nested" type Address struct { City string `json:"city,omitempty"` @@ -13,8 +11,8 @@ type Address struct { ArrayArrayAddress [][]*Address People []Person MapCrap map[string]map[int]bool - NestedPtr *nested.Nested - NestedStruct nested.Nested + NestedPtr *nstd.Nested + NestedStruct nstd.Nested } type Person struct { @@ -43,11 +41,11 @@ func (s *Service) TestScalarInPlace() ScalarInPlace { return ScalarInPlace("hier") } -func (s *Service) Nest() *nested.Nested { +func (s *Service) Nest() *nstd.Nested { return nil } -//func (s *Service) GiveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { -func (s *Service) giveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace) { - return nested.Amount(10), nested.ItIsTrue, ScalarInPlace("hier") +func (s *Service) GiveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { + //func (s *Service) giveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { + return nstd.Amount(10), nstd.ItIsTrue, ScalarInPlace("hier") } diff --git a/demo/config.yml b/demo/config.yml index 2632bc9..2422233 100644 --- a/demo/config.yml +++ b/demo/config.yml @@ -3,7 +3,7 @@ targets: demo: module: GoTSRPC.Demo services: - - Service + /service/demo: Service package: github.com/foomo/gotsrpc/demo out: /tmp/test.ts mappings: diff --git a/demo/gotsrpc.go b/demo/gotsrpc.go index 4826a3c..c295e2f 100644 --- a/demo/gotsrpc.go +++ b/demo/gotsrpc.go @@ -3,7 +3,9 @@ package demo import ( gotsrpc "github.com/foomo/gotsrpc" + nested "github.com/foomo/gotsrpc/demo/nested" http "net/http" + time "time" ) type ServiceGoTSRPCProxy struct { @@ -12,6 +14,14 @@ type ServiceGoTSRPCProxy struct { service *Service } +func NewDefaultServiceGoTSRPCProxy(service *Service, allowOrigin []string) *ServiceGoTSRPCProxy { + return &ServiceGoTSRPCProxy{ + EndPoint: "/service/demo", + allowOrigin: allowOrigin, + service: service, + } +} + func NewServiceGoTSRPCProxy(service *Service, endpoint string, allowOrigin []string) *ServiceGoTSRPCProxy { return &ServiceGoTSRPCProxy{ EndPoint: endpoint, @@ -24,45 +34,131 @@ func NewServiceGoTSRPCProxy(service *Service, endpoint string, allowOrigin []str func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { for _, origin := range p.allowOrigin { + // todo we have to compare this with the referer ... and only send one w.Header().Add("Access-Control-Allow-Origin", origin) } w.Header().Set("Access-Control-Allow-Credentials", "true") - if r.Method != "POST" { + if r.Method != http.MethodPost { + if r.Method == http.MethodOptions { + return + } gotsrpc.ErrorMethodNotAllowed(w) return } var args []interface{} - switch gotsrpc.GetCalledFunc(r, p.EndPoint) { - case "Hello": - args = []interface{}{""} - err := gotsrpc.LoadArgs(args, r) - if err != nil { - gotsrpc.ErrorCouldNotLoadArgs(w) - return - } - helloReply, helloErr := p.service.Hello(args[0].(string)) - gotsrpc.Reply([]interface{}{helloReply, helloErr}, w) - return + funcName := gotsrpc.GetCalledFunc(r, p.EndPoint) + callStats := gotsrpc.GetStatsForRequest(r) + if callStats != nil { + callStats.Func = funcName + callStats.Package = "github.com/foomo/gotsrpc/demo" + callStats.Service = "Service" + } + switch funcName { case "ExtractAddress": args = []interface{}{&Person{}} - err := gotsrpc.LoadArgs(args, r) + err := gotsrpc.LoadArgs(args, callStats, r) if err != nil { gotsrpc.ErrorCouldNotLoadArgs(w) return } + executionStart := time.Now() extractAddressAddr, extractAddressE := p.service.ExtractAddress(args[0].(*Person)) - gotsrpc.Reply([]interface{}{extractAddressAddr, extractAddressE}, w) + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{extractAddressAddr, extractAddressE}, callStats, r, w) return - case "TestScalarInPlace": - testScalarInPlaceRet := p.service.TestScalarInPlace() - gotsrpc.Reply([]interface{}{testScalarInPlaceRet}, w) + case "GiveMeAScalar": + executionStart := time.Now() + giveMeAScalarAmount, giveMeAScalarWahr, giveMeAScalarHier := p.service.GiveMeAScalar() + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{giveMeAScalarAmount, giveMeAScalarWahr, giveMeAScalarHier}, callStats, r, w) + return + case "Hello": + args = []interface{}{""} + err := gotsrpc.LoadArgs(args, callStats, r) + if err != nil { + gotsrpc.ErrorCouldNotLoadArgs(w) + return + } + executionStart := time.Now() + helloReply, helloErr := p.service.Hello(args[0].(string)) + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{helloReply, helloErr}, callStats, r, w) return case "Nest": + executionStart := time.Now() nestRet := p.service.Nest() - gotsrpc.Reply([]interface{}{nestRet}, w) + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{nestRet}, callStats, r, w) + return + case "TestScalarInPlace": + executionStart := time.Now() + testScalarInPlaceRet := p.service.TestScalarInPlace() + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{testScalarInPlaceRet}, callStats, r, w) return default: http.Error(w, "404 - not found "+r.URL.Path, http.StatusNotFound) } } + +type ServiceGoTSRPCClient struct { + URL string + EndPoint string +} + +func NewDefaultServiceGoTSRPCClient(url string) *ServiceGoTSRPCClient { + return NewServiceGoTSRPCClient(url, "/service/demo") +} + +func NewServiceGoTSRPCClient(url string, endpoint string) *ServiceGoTSRPCClient { + return &ServiceGoTSRPCClient{ + URL: url, + EndPoint: endpoint, + } +} + +func (c *ServiceGoTSRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { + args := []interface{}{person} + reply := []interface{}{&addr, &e} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "ExtractAddress", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { + args := []interface{}{} + reply := []interface{}{&amount, &wahr, &hier} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "GiveMeAScalar", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { + args := []interface{}{name} + reply := []interface{}{&reply, &err} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Hello", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { + args := []interface{}{} + reply := []interface{}{&retNest_0} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Nest", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { + args := []interface{}{} + reply := []interface{}{&retTestScalarInPlace_0} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "TestScalarInPlace", args, reply) + return +} From 3206fd98e8c54df4f36bf30476fafa9d2905a546 Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 15:55:02 +0100 Subject: [PATCH 07/23] renamed err to clientErr and addd scalar support --- go.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/go.go b/go.go index f05c377..d620f99 100644 --- a/go.go +++ b/go.go @@ -26,10 +26,17 @@ func (v *Value) goType(aliases map[string]string, packageName string) (t string) t += aliases[v.StructType.Package] + "." } t += v.StructType.Name - + case v.Scalar != nil: + // TODO this is a hack to retrieve string types + if packageName != v.Scalar.Package { + t += aliases[v.Scalar.Package] + "." + } + t += v.Scalar.Name[len(v.Scalar.Package)+1:] + default: + // TODO } - return + return } func (v *Value) emptyLiteral(aliases map[string]string) (e string) { @@ -346,12 +353,12 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, rets = append(rets, "&"+name) returns = append(returns, name+" "+r.Value.goType(aliases, fullPackageName)) } - returns = append(returns, "err error") + returns = append(returns, "clientErr error") g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) g.ind(1) g.l(`args := []interface{}{` + strings.Join(args, ", ") + `}`) g.l(`reply := []interface{}{` + strings.Join(rets, ", ") + `}`) - g.l(`err = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) + g.l(`clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) g.l(`return`) g.ind(-1) g.l(`}`) From b891b771086f03670f5b2c4cba8c0cc15e835adc Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 15:56:22 +0100 Subject: [PATCH 08/23] added support for selectorExpr and StructType on arrayType --- typereader.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/typereader.go b/typereader.go index d33e622..45f9e9c 100644 --- a/typereader.go +++ b/typereader.go @@ -204,25 +204,27 @@ func (v *Value) loadExpr(expr ast.Expr, fileImports fileImportSpecMap) { v.Array = &Array{Value: &Value{}} switch reflect.ValueOf(fieldArray.Elt).Type().String() { + case "*ast.ArrayType": + //readAstArrayType(v.Array.Value, fieldArray.Elt.(*ast.ArrayType), fileImports) + v.Array.Value.loadExpr(fieldArray.Elt.(*ast.ArrayType), fileImports) case "*ast.Ident": readAstType(v.Array.Value, fieldArray.Elt.(*ast.Ident), fileImports) case "*ast.StarExpr": readAstStarExpr(v.Array.Value, fieldArray.Elt.(*ast.StarExpr), fileImports) - case "*ast.ArrayType": - //readAstArrayType(v.Array.Value, fieldArray.Elt.(*ast.ArrayType), fileImports) - v.Array.Value.loadExpr(fieldArray.Elt.(*ast.ArrayType), fileImports) case "*ast.MapType": v.Array.Value.Map = &Map{ Value: &Value{}, } readAstMapType(v.Array.Value.Map, fieldArray.Elt.(*ast.MapType), fileImports) + case "*ast.SelectorExpr": + readAstSelectorExpr(v.Array.Value, fieldArray.Elt.(*ast.SelectorExpr), fileImports) + case "*ast.StructType": + readAstStructType(v.Array.Value, fieldArray.Elt.(*ast.StructType), fileImports) default: trace("---------------------> array of", reflect.ValueOf(fieldArray.Elt).Type().String()) } case "*ast.Ident": - fieldIdent := expr.(*ast.Ident) - readAstType(v, fieldIdent, fileImports) case "*ast.StarExpr": // a pointer on sth From c4193cf8d8ad757c3ed1bc7c0e335b77aa633e34 Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 14 Dec 2016 17:31:24 +0100 Subject: [PATCH 09/23] fixed import extraction on array types --- go.go | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/go.go b/go.go index d620f99..600d837 100644 --- a/go.go +++ b/go.go @@ -133,27 +133,32 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, "github.com/foomo/gotsrpc": "gotsrpc", } r := strings.NewReplacer(".", "_", "/", "_", "-", "_") + + extractImport := func(st *StructType) { + if st.Package != fullPackageName { + alias, ok := aliases[st.Package] + if !ok { + packageParts := strings.Split(st.Package, "/") + beautifulAlias := packageParts[len(packageParts)-1] + uglyAlias := r.Replace(st.Package) + alias = beautifulAlias + for _, otherAlias := range aliases { + if otherAlias == beautifulAlias { + alias = uglyAlias + break + } + } + aliases[st.Package] = alias + } + } + } + extractImports := func(fields []*Field) { for _, f := range fields { if f.Value.StructType != nil { - st := f.Value.StructType - if st.Package != fullPackageName { - alias, ok := aliases[st.Package] - if !ok { - packageParts := strings.Split(st.Package, "/") - beautifulAlias := packageParts[len(packageParts)-1] - uglyAlias := r.Replace(st.Package) - alias = beautifulAlias - for _, otherAlias := range aliases { - if otherAlias == beautifulAlias { - alias = uglyAlias - break - } - } - aliases[st.Package] = alias - } - - } + extractImport(f.Value.StructType) + } else if f.Value.Array != nil && f.Value.Array.Value.StructType != nil { + extractImport(f.Value.Array.Value.StructType) } } } From 4db5128e6850e3664117656e0e3e1af78b853029 Mon Sep 17 00:00:00 2001 From: franklin Date: Sat, 17 Dec 2016 15:44:45 +0100 Subject: [PATCH 10/23] added glide --- .gitignore | 2 +- build.go | 67 ++++++-- config/config.go | 7 +- demo/config.yml | 3 +- demo/gotsrpc.go | 52 ------ demo/gotsrpc_test.go | 1 - glide.lock | 36 ++++ glide.yaml | 8 + go.go | 352 ++++++++++++++++++++++++++++++++++++--- gorpc.go | 4 + prometheus/prometheus.go | 14 ++ 11 files changed, 452 insertions(+), 94 deletions(-) delete mode 100644 demo/gotsrpc_test.go create mode 100644 glide.lock create mode 100644 glide.yaml create mode 100644 gorpc.go diff --git a/.gitignore b/.gitignore index 01c3a9a..7bcf902 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .* !.git* - +/vendor/ diff --git a/build.go b/build.go index 518efb6..ef31b54 100644 --- a/build.go +++ b/build.go @@ -57,13 +57,22 @@ func Build(conf *config.Config, goPath string) { fmt.Fprintln(os.Stderr, "building target", name) longPackageName := target.Package longPackageNameParts := strings.Split(longPackageName, "/") - goFilename := path.Join(goPath, "src", longPackageName, "gotsrpc.go") + goRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gorpc.go") + goRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gorpcclient.go") + goTSRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gotsrpc.go") + goTSRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gotsrpcclient.go") - _, err := os.Stat(goFilename) - if err == nil { - fmt.Fprintln(os.Stderr, " removing existing", goFilename) - os.Remove(goFilename) + remove := func(filename string) { + _, err := os.Stat(filename) + if err == nil { + fmt.Fprintln(os.Stderr, " removing existing", filename) + os.Remove(filename) + } } + remove(goRPCProxiesFilename) + remove(goRPCClientsFilename) + remove(goTSRPCProxiesFilename) + remove(goTSRPCClientsFilename) packageName := longPackageNameParts[len(longPackageNameParts)-1] @@ -98,23 +107,49 @@ func Build(conf *config.Config, goPath string) { os.Exit(4) } - gocode, goerr := RenderGo(services, longPackageName, packageName) + formatAndWrite := func(code string, filename string) { + formattedGoBytes, formattingError := format.Source([]byte(code)) + if formattingError == nil { + code = string(formattedGoBytes) + } else { + fmt.Fprintln(os.Stderr, " could not format go ts rpc proxies code", formattingError) + } + + writeErr := ioutil.WriteFile(filename, []byte(code), 0644) + if writeErr != nil { + fmt.Fprintln(os.Stderr, " could not write go source to file", writeErr) + os.Exit(5) + } + } + + goTSRPCProxiesCode, goerr := RenderGoTSRPCProxies(services, longPackageName, packageName) if goerr != nil { - fmt.Fprintln(os.Stderr, " could not generate go code in target", name, goerr) + fmt.Fprintln(os.Stderr, " could not generate go ts rpc proxies code in target", name, goerr) os.Exit(4) } + formatAndWrite(goTSRPCProxiesCode, goTSRPCProxiesFilename) - formattedGoBytes, formattingError := format.Source([]byte(gocode)) - if formattingError == nil { - gocode = string(formattedGoBytes) - } else { - fmt.Fprintln(os.Stderr, " could not format go code", formattingError) + goTSRPCClientsCode, goerr := RenderGoTSRPCClients(services, longPackageName, packageName) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate go ts rpc clients code in target", name, goerr) + os.Exit(4) } + formatAndWrite(goTSRPCClientsCode, goTSRPCClientsFilename) - writeErr := ioutil.WriteFile(goFilename, []byte(gocode), 0644) - if writeErr != nil { - fmt.Fprintln(os.Stderr, " could not write go source to file", writeErr) - os.Exit(5) + if target.RPC == true { + goRPCProxiesCode, goerr := RenderGoRPCProxies(services, longPackageName, packageName) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate go rpc proxies code in target", name, goerr) + os.Exit(4) + } + formatAndWrite(goRPCProxiesCode, goRPCProxiesFilename) + + goRPCClientsCode, goerr := RenderGoRPCClients(services, longPackageName, packageName) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate go rpc clients code in target", name, goerr) + os.Exit(4) + } + formatAndWrite(goRPCClientsCode, goRPCClientsFilename) } } // spew.Dump(mappedTypeScript) diff --git a/config/config.go b/config/config.go index e1b2ee1..8e94952 100644 --- a/config/config.go +++ b/config/config.go @@ -9,10 +9,11 @@ import ( ) type Target struct { - Package string - Services map[string]string + Package string `yaml:"package"` + Services map[string]string `yaml:"services"` TypeScriptModule string `yaml:"module"` - Out string + Out string `yaml:"out"` + RPC bool `yaml:"rpc"` } type Mapping struct { diff --git a/demo/config.yml b/demo/config.yml index 2422233..9778152 100644 --- a/demo/config.yml +++ b/demo/config.yml @@ -5,7 +5,8 @@ targets: services: /service/demo: Service package: github.com/foomo/gotsrpc/demo - out: /tmp/test.ts + out: /tmp/test.ts + rpc: true mappings: github.com/foomo/gotsrpc/demo: module: GoTSRPC.Demo diff --git a/demo/gotsrpc.go b/demo/gotsrpc.go index c295e2f..d6e6e3f 100644 --- a/demo/gotsrpc.go +++ b/demo/gotsrpc.go @@ -3,7 +3,6 @@ package demo import ( gotsrpc "github.com/foomo/gotsrpc" - nested "github.com/foomo/gotsrpc/demo/nested" http "net/http" time "time" ) @@ -111,54 +110,3 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) http.Error(w, "404 - not found "+r.URL.Path, http.StatusNotFound) } } - -type ServiceGoTSRPCClient struct { - URL string - EndPoint string -} - -func NewDefaultServiceGoTSRPCClient(url string) *ServiceGoTSRPCClient { - return NewServiceGoTSRPCClient(url, "/service/demo") -} - -func NewServiceGoTSRPCClient(url string, endpoint string) *ServiceGoTSRPCClient { - return &ServiceGoTSRPCClient{ - URL: url, - EndPoint: endpoint, - } -} - -func (c *ServiceGoTSRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { - args := []interface{}{person} - reply := []interface{}{&addr, &e} - clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "ExtractAddress", args, reply) - return -} - -func (c *ServiceGoTSRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { - args := []interface{}{} - reply := []interface{}{&amount, &wahr, &hier} - clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "GiveMeAScalar", args, reply) - return -} - -func (c *ServiceGoTSRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { - args := []interface{}{name} - reply := []interface{}{&reply, &err} - clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Hello", args, reply) - return -} - -func (c *ServiceGoTSRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { - args := []interface{}{} - reply := []interface{}{&retNest_0} - clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Nest", args, reply) - return -} - -func (c *ServiceGoTSRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { - args := []interface{}{} - reply := []interface{}{&retTestScalarInPlace_0} - clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "TestScalarInPlace", args, reply) - return -} diff --git a/demo/gotsrpc_test.go b/demo/gotsrpc_test.go deleted file mode 100644 index 2da2349..0000000 --- a/demo/gotsrpc_test.go +++ /dev/null @@ -1 +0,0 @@ -package demo_test diff --git a/glide.lock b/glide.lock new file mode 100644 index 0000000..ddbb8de --- /dev/null +++ b/glide.lock @@ -0,0 +1,36 @@ +hash: ceb81b420e31dbc1d0fac128b688dfb563ab5fc6e0220d995dc20830e7a127c8 +updated: 2016-12-17T07:08:03.465146982+01:00 +imports: +- name: github.com/beorn7/perks + version: 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 + subpackages: + - quantile +- name: github.com/golang/protobuf + version: da116c3771bf4a398a43f44e069195ef1c9688ef + subpackages: + - proto +- name: github.com/matttproud/golang_protobuf_extensions + version: c12348ce28de40eed0136aa2b644d0ee0650e56c + subpackages: + - pbutil +- name: github.com/prometheus/client_golang + version: c5b7fccd204277076155f10851dad72b76a49317 + subpackages: + - prometheus +- name: github.com/prometheus/client_model + version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6 + subpackages: + - go +- name: github.com/prometheus/common + version: 0d5de9d6d8629cb8bee6d4674da4127cd8b615a3 + subpackages: + - expfmt + - internal/bitbucket.org/ww/goautoneg + - model +- name: github.com/prometheus/procfs + version: abf152e5f3e97f2fafac028d2cc06c1feb87ffa5 +- name: github.com/valyala/gorpc + version: 908281bef77441f2d0ec1792189b4032a1dace0c +- name: gopkg.in/yaml.v2 + version: a5b47d31c556af34a302ce5d659e6fea44d90de0 +testImports: [] diff --git a/glide.yaml b/glide.yaml new file mode 100644 index 0000000..ed3fc8d --- /dev/null +++ b/glide.yaml @@ -0,0 +1,8 @@ +package: github.com/foomo/gotsrpc +import: +- package: github.com/prometheus/client_golang + version: ~0.8.0 + subpackages: + - prometheus +- package: github.com/valyala/gorpc +- package: gopkg.in/yaml.v2 diff --git a/go.go b/go.go index 600d837..f358d66 100644 --- a/go.go +++ b/go.go @@ -8,6 +8,7 @@ import ( func (v *Value) isHTTPResponseWriter() bool { return v.StructType != nil && v.StructType.Name == "ResponseWriter" && v.StructType.Package == "net/http" } + func (v *Value) isHTTPRequest() bool { return v.IsPtr && v.StructType != nil && v.StructType.Name == "Request" && v.StructType.Package == "net/http" } @@ -126,12 +127,8 @@ func strfirst(str string, strfunc func(string) string) string { } -func renderServiceProxies(services map[string]*Service, fullPackageName string, packageName string, g *code) error { - aliases := map[string]string{ - "net/http": "http", - "time": "time", - "github.com/foomo/gotsrpc": "gotsrpc", - } +func extractImports(fields []*Field, fullPackageName string, aliases map[string]string) { + r := strings.NewReplacer(".", "_", "/", "_", "-", "_") extractImport := func(st *StructType) { @@ -153,19 +150,25 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, } } - extractImports := func(fields []*Field) { - for _, f := range fields { - if f.Value.StructType != nil { - extractImport(f.Value.StructType) - } else if f.Value.Array != nil && f.Value.Array.Value.StructType != nil { - extractImport(f.Value.Array.Value.StructType) - } + for _, f := range fields { + if f.Value.StructType != nil { + extractImport(f.Value.StructType) + } else if f.Value.Array != nil && f.Value.Array.Value.StructType != nil { + extractImport(f.Value.Array.Value.StructType) } } +} + +func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, g *code) error { + aliases := map[string]string{ + "time": "time", + "net/http": "http", + "github.com/foomo/gotsrpc": "gotsrpc", + } + for _, s := range services { for _, m := range s.Methods { - extractImports(m.Args) - extractImports(m.Return) + extractImports(m.Args, fullPackageName, aliases) } } @@ -206,7 +209,7 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, service: service, } } - + // ServeHTTP exposes your service func (p *` + proxyName + `) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -322,7 +325,35 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, g.ind(1).l("http.Error(w, \"404 - not found \" + r.URL.Path, http.StatusNotFound)") g.ind(-2).l("}") // close switch g.ind(-1).l("}") // close ServeHttp + } + return nil +} +func renderTSRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, g *code) error { + aliases := map[string]string{ + "github.com/foomo/gotsrpc": "gotsrpc", + } + + for _, s := range services { + for _, m := range s.Methods { + extractImports(m.Args, fullPackageName, aliases) + extractImports(m.Return, fullPackageName, aliases) + } + } + + imports := "" + for packageName, alias := range aliases { + imports += alias + " \"" + packageName + "\"\n" + } + + g.l(` + // this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc + package ` + packageName + ` + import ( + ` + imports + ` + ) + `) + for endpoint, service := range services { clientName := service.Name + "GoTSRPCClient" g.l(` type ` + clientName + ` struct { @@ -360,12 +391,10 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, } returns = append(returns, "clientErr error") g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) - g.ind(1) g.l(`args := []interface{}{` + strings.Join(args, ", ") + `}`) g.l(`reply := []interface{}{` + strings.Join(rets, ", ") + `}`) g.l(`clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) g.l(`return`) - g.ind(-1) g.l(`}`) g.nl() } @@ -373,9 +402,292 @@ func renderServiceProxies(services map[string]*Service, fullPackageName string, return nil } -func RenderGo(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { +func renderRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, g *code) error { + aliases := map[string]string{ + "fmt": "fmt", + "time": "time", + "strings": "strings", + "reflect": "reflect", + "crypto/tls": "tls", + "encoding/gob": "gob", + "github.com/valyala/gorpc": "gorpc", + "github.com/foomo/gotsrpc": "gotsrpc", + } + + for _, s := range services { + for _, m := range s.Methods { + extractImports(m.Args, fullPackageName, aliases) + extractImports(m.Return, fullPackageName, aliases) + } + } + + imports := "" + for packageName, alias := range aliases { + imports += alias + " \"" + packageName + "\"\n" + } + + g.l(` + // this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc + package ` + packageName + ` + import ( + ` + imports + ` + ) + `) + for _, service := range services { + proxyName := service.Name + "GoRPCProxy" + // Types + g.l(`type (`) + // Proxy type + g.l(` + ` + proxyName + ` struct { + server *gorpc.Server + service *` + service.Name + ` + callStatsHandler *gotsrpc.GoRPCCallStatsHandlerFun + } + `) + // Request & Response types + for _, method := range service.Methods { + // Request type + g.l(ucfirst(method.Name) + `Request struct {`) + for _, a := range method.Args { + g.l(ucfirst(a.Name) + ` ` + a.Value.goType(aliases, fullPackageName)) + } + g.l(`}`) + // Response type + g.l(ucfirst(method.Name) + `Response struct {`) + for i, r := range method.Return { + name := r.Name + if len(name) == 0 { + name = fmt.Sprintf("ret%s_%d", method.Name, i) + } + g.l(ucfirst(name) + ` ` + r.Value.goType(aliases, fullPackageName)) + } + g.l(`}`) + g.nl() + } + g.l(`)`) + g.nl() + // Init + g.l(`func init() {`) + for _, method := range service.Methods { + g.l(`gob.Register(` + ucfirst(method.Name) + `Request{})`) + g.l(`gob.Register(` + ucfirst(method.Name) + `Response{})`) + } + g.l(`}`) + // Constructor + g.l(` + func New` + proxyName + `(addr string, service *` + service.Name + `, tlsConfig *tls.Config) *` + proxyName + ` { + proxy := &` + proxyName + `{ + service: service, + } + + if tlsConfig != nil { + proxy.server = gorpc.NewTLSServer(addr, proxy.handler, tlsConfig) + } else { + proxy.server = gorpc.NewTCPServer(addr, proxy.handler) + } + + return proxy + } + + func (p *` + proxyName + `) Start() error { + return p.server.Start() + } + + func (p *` + proxyName + `) Stop() { + p.server.Stop() + } + + func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler *gotsrpc.GoRPCCallStatsHandlerFun) { + p.callStatsHandler = handler + } + `) + g.nl() + // Handler + g.l(`func (p *` + proxyName + `) handler(clientAddr string, request interface{}) (response interface{}) {`) + g.l(`start := time.Now()`) + g.nl() + g.l(`reqType := reflect.TypeOf(request).String()`) + g.l(`funcNameParts := strings.Split(reqType, ".")`) + g.l(`funcName := funcNameParts[len(funcNameParts)-1]`) + g.nl() + g.l(`switch funcName {`) + for _, method := range service.Methods { + argParams := []string{} + for _, a := range method.Args { + argParams = append(argParams, "req."+ucfirst(a.Name)) + } + rets := []string{} + retParams := []string{} + for i, r := range method.Return { + name := r.Name + if len(name) == 0 { + name = fmt.Sprintf("ret%s_%d", method.Name, i) + } + rets = append(rets, name) + retParams = append(retParams, ucfirst(name)+`: `+name) + } + g.l(`case "` + method.Name + `Request":`) + if len(argParams) > 0 { + g.l(`req := request.(` + method.Name + `Request)`) + } + if len(rets) > 0 { + g.l(strings.Join(rets, ", ") + ` := p.service.` + method.Name + `(`+strings.Join(argParams, ", ")+`)`) + } else { + g.l(`p.service.` + method.Name + `(`+strings.Join(argParams, ", ")+`)`) + } + g.l(`response = ` + method.Name + `Response{`+strings.Join(retParams, ", ")+`}`) + } + g.l(`default:`) + g.l(`fmt.Println("Unkown request type", reflect.TypeOf(request).String())`) + g.l(`}`) + g.nl() + g.l(`if p.callStatsHandler != nil {`) + g.l(`*p.callStatsHandler(gotsrpc.CallStats{`) + g.l(`Func: funcName,`) + g.l(`Package: "` + fullPackageName + `",`) + g.l(`Service: "` + service.Name + `",`) + g.l(`Execution: time.Since(start),`) + g.l(`})`) + g.l(`}`) + g.nl() + g.l(`return`) + g.l(`}`) + } + return nil +} + +func renderRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, g *code) error { + aliases := map[string]string{ + "crypto/tls": "tls", + "github.com/valyala/gorpc": "gorpc", + } + + for _, s := range services { + for _, m := range s.Methods { + extractImports(m.Args, fullPackageName, aliases) + extractImports(m.Return, fullPackageName, aliases) + } + } + + imports := "" + for packageName, alias := range aliases { + imports += alias + " \"" + packageName + "\"\n" + } + + g.l(` + // this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc + package ` + packageName + ` + import ( + ` + imports + ` + ) + `) + for _, service := range services { + clientName := service.Name + "GoRPCClient" + // Client type + g.l(` + type ` + clientName + ` struct { + client *gorpc.Client + } + `) + // Constructor + g.l(` + func New` + clientName + `(addr string, tlsConfig *tls.Config) *` + clientName + ` { + client := &` + clientName + `{} + if tlsConfig == nil { + client.client = gorpc.NewTCPClient(addr) + } else { + client.client = gorpc.NewTLSClient(addr, tlsConfig) + } + client.Start() + return client + } + + func (c *` + clientName + `) Start() { + c.client.Start() + } + + func (c *` + clientName + `) Stop() { + c.client.Stop() + } + `) + g.nl() + // Methods + for _, method := range service.Methods { + args := []string{} + params := []string{} + for _, a := range method.Args { + args = append(args, ucfirst(a.Name) + `: ` + a.Name) + params = append(params, a.Name + " " + a.Value.goType(aliases, fullPackageName)) + } + rets := []string{} + returns := []string{} + for i, r := range method.Return { + name := r.Name + if len(name) == 0 { + name = fmt.Sprintf("ret%s_%d", method.Name, i) + } + rets = append(rets, "response." + ucfirst(name)) + returns = append(returns, name + " " + r.Value.goType(aliases, fullPackageName)) + } + returns = append(returns, "clientErr error") + g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) + g.l(`req := ` + method.Name + `Request{` + strings.Join(args, ", ") + `}`) + if len(rets) > 0 { + g.l(`res, err := c.client.Call(req)`) + } else { + g.l(`_, err := c.client.Call(req)`) + } + g.l(`if err != nil {`) + g.l(`clientErr = err`) + g.l(`return`) + g.l(`}`) + if len(rets) > 0 { + g.l(`response := res.(` + method.Name + `Response)`) + g.l(`return ` + strings.Join(rets, ", ") + `, nil`) + } else { + g.l(`return nil`) + } + g.l(`}`) + g.nl() + } + } + return nil +} + +func RenderGoTSRPCProxies(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { g := newCode(" ") - err = renderServiceProxies(services, longPackageName, packageName, g) + err = renderTSRPCServiceProxies(services, longPackageName, packageName, g) + if err != nil { + return + } + gocode = g.string() + return +} + +func RenderGoTSRPCClients(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { + g := newCode(" ") + err = renderTSRPCServiceClients(services, longPackageName, packageName, g) + if err != nil { + return + } + gocode = g.string() + return +} + +func RenderGoRPCProxies(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { + g := newCode(" ") + err = renderRPCServiceProxies(services, longPackageName, packageName, g) + if err != nil { + return + } + gocode = g.string() + return +} + +func RenderGoRPCClients(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { + g := newCode(" ") + err = renderRPCServiceClients(services, longPackageName, packageName, g) if err != nil { return } diff --git a/gorpc.go b/gorpc.go new file mode 100644 index 0000000..9cd4356 --- /dev/null +++ b/gorpc.go @@ -0,0 +1,4 @@ +package gotsrpc + + +type GoRPCCallStatsHandlerFun func(stats CallStats) \ No newline at end of file diff --git a/prometheus/prometheus.go b/prometheus/prometheus.go index 589ec41..b246674 100644 --- a/prometheus/prometheus.go +++ b/prometheus/prometheus.go @@ -27,3 +27,17 @@ func InstrumentService(s http.HandlerFunc) (handler http.HandlerFunc) { } } } + +func InstrumentGoRPCService() { + callsCounter := p.NewSummaryVec(p.SummaryOpts{ + Namespace: "gorpc", + Subsystem: "service", + Name: "time_seconds", + Help: "seconds to execute a service method", + }, []string{"package", "service", "func", "type"}) + p.MustRegister(callsCounter) + + return func(stats *gotsrpc.CallStats) { + callsCounter.WithLabelValues(stats.Package, stats.Service, stats.Func, "execution").Observe(float64(stats.Execution)) + } +} From 777547646f3fb5963024fd8ed4b820a2c8a4b640 Mon Sep 17 00:00:00 2001 From: franklin Date: Sat, 17 Dec 2016 15:45:16 +0100 Subject: [PATCH 11/23] added gorpc generator --- demo/gorpc.go | 138 ++++++++++++++++++++++++++++++++++++++++++ demo/gorpcclient.go | 86 ++++++++++++++++++++++++++ demo/gotsrpcclient.go | 58 ++++++++++++++++++ 3 files changed, 282 insertions(+) create mode 100644 demo/gorpc.go create mode 100644 demo/gorpcclient.go create mode 100644 demo/gotsrpcclient.go diff --git a/demo/gorpc.go b/demo/gorpc.go new file mode 100644 index 0000000..7275a8a --- /dev/null +++ b/demo/gorpc.go @@ -0,0 +1,138 @@ +// this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc +package demo + +import ( + tls "crypto/tls" + gob "encoding/gob" + fmt "fmt" + gotsrpc "github.com/foomo/gotsrpc" + nested "github.com/foomo/gotsrpc/demo/nested" + gorpc "github.com/valyala/gorpc" + reflect "reflect" + strings "strings" + time "time" +) + +type ( + ServiceGoRPCProxy struct { + server *gorpc.Server + service *Service + callStatsHandler *gotsrpc.GoRPCCallStatsHandlerFun + } + + ExtractAddressRequest struct { + Person *Person + } + ExtractAddressResponse struct { + Addr *Address + E *Err + } + + GiveMeAScalarRequest struct { + } + GiveMeAScalarResponse struct { + Amount nested.Amount + Wahr nested.True + Hier ScalarInPlace + } + + HelloRequest struct { + Name string + } + HelloResponse struct { + Reply string + Err *Err + } + + NestRequest struct { + } + NestResponse struct { + RetNest_0 *nested.Nested + } + + TestScalarInPlaceRequest struct { + } + TestScalarInPlaceResponse struct { + RetTestScalarInPlace_0 ScalarInPlace + } +) + +func init() { + gob.Register(ExtractAddressRequest{}) + gob.Register(ExtractAddressResponse{}) + gob.Register(GiveMeAScalarRequest{}) + gob.Register(GiveMeAScalarResponse{}) + gob.Register(HelloRequest{}) + gob.Register(HelloResponse{}) + gob.Register(NestRequest{}) + gob.Register(NestResponse{}) + gob.Register(TestScalarInPlaceRequest{}) + gob.Register(TestScalarInPlaceResponse{}) +} + +func NewServiceGoRPCProxy(addr string, service *Service, tlsConfig *tls.Config) *ServiceGoRPCProxy { + proxy := &ServiceGoRPCProxy{ + service: service, + } + + if tlsConfig != nil { + proxy.server = gorpc.NewTLSServer(addr, proxy.handler, tlsConfig) + } else { + proxy.server = gorpc.NewTCPServer(addr, proxy.handler) + } + + return proxy +} + +func (p *ServiceGoRPCProxy) Start() error { + return p.server.Start() +} + +func (p *ServiceGoRPCProxy) Stop() { + p.server.Stop() +} + +func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler *gotsrpc.GoRPCCallStatsHandlerFun) { + p.callStatsHandler = handler +} + +func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (response interface{}) { + start := time.Now() + + reqType := reflect.TypeOf(request).String() + funcNameParts := strings.Split(reqType, ".") + funcName := funcNameParts[len(funcNameParts)-1] + + switch funcName { + case "ExtractAddressRequest": + req := request.(ExtractAddressRequest) + addr, e := p.service.ExtractAddress(req.Person) + response = ExtractAddressResponse{Addr: addr, E: e} + case "GiveMeAScalarRequest": + amount, wahr, hier := p.service.GiveMeAScalar() + response = GiveMeAScalarResponse{Amount: amount, Wahr: wahr, Hier: hier} + case "HelloRequest": + req := request.(HelloRequest) + reply, err := p.service.Hello(req.Name) + response = HelloResponse{Reply: reply, Err: err} + case "NestRequest": + retNest_0 := p.service.Nest() + response = NestResponse{RetNest_0: retNest_0} + case "TestScalarInPlaceRequest": + retTestScalarInPlace_0 := p.service.TestScalarInPlace() + response = TestScalarInPlaceResponse{RetTestScalarInPlace_0: retTestScalarInPlace_0} + default: + fmt.Println("Unkown request type", reflect.TypeOf(request).String()) + } + + if p.callStatsHandler != nil { + *p.callStatsHandler(gotsrpc.CallStats{ + Func: funcName, + Package: "github.com/foomo/gotsrpc/demo", + Service: "Service", + Execution: time.Since(start), + }) + } + + return +} diff --git a/demo/gorpcclient.go b/demo/gorpcclient.go new file mode 100644 index 0000000..a8a2001 --- /dev/null +++ b/demo/gorpcclient.go @@ -0,0 +1,86 @@ +// this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc +package demo + +import ( + tls "crypto/tls" + nested "github.com/foomo/gotsrpc/demo/nested" + gorpc "github.com/valyala/gorpc" +) + +type ServiceGoRPCClient struct { + client *gorpc.Client +} + +func NewServiceGoRPCClient(addr string, tlsConfig *tls.Config) *ServiceGoRPCClient { + client := &ServiceGoRPCClient{} + if tlsConfig == nil { + client.client = gorpc.NewTCPClient(addr) + } else { + client.client = gorpc.NewTLSClient(addr, tlsConfig) + } + client.Start() + return client +} + +func (c *ServiceGoRPCClient) Start() { + c.client.Start() +} + +func (c *ServiceGoRPCClient) Stop() { + c.client.Stop() +} + +func (c *ServiceGoRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { + req := ExtractAddressRequest{Person: person} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(ExtractAddressResponse) + return response.Addr, response.E, nil +} + +func (c *ServiceGoRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { + req := GiveMeAScalarRequest{} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(GiveMeAScalarResponse) + return response.Amount, response.Wahr, response.Hier, nil +} + +func (c *ServiceGoRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { + req := HelloRequest{Name: name} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(HelloResponse) + return response.Reply, response.Err, nil +} + +func (c *ServiceGoRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { + req := NestRequest{} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(NestResponse) + return response.RetNest_0, nil +} + +func (c *ServiceGoRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { + req := TestScalarInPlaceRequest{} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(TestScalarInPlaceResponse) + return response.RetTestScalarInPlace_0, nil +} diff --git a/demo/gotsrpcclient.go b/demo/gotsrpcclient.go new file mode 100644 index 0000000..dfa5ed2 --- /dev/null +++ b/demo/gotsrpcclient.go @@ -0,0 +1,58 @@ +// this file was auto generated by gotsrpc https://github.com/foomo/gotsrpc +package demo + +import ( + gotsrpc "github.com/foomo/gotsrpc" + nested "github.com/foomo/gotsrpc/demo/nested" +) + +type ServiceGoTSRPCClient struct { + URL string + EndPoint string +} + +func NewDefaultServiceGoTSRPCClient(url string) *ServiceGoTSRPCClient { + return NewServiceGoTSRPCClient(url, "/service/demo") +} + +func NewServiceGoTSRPCClient(url string, endpoint string) *ServiceGoTSRPCClient { + return &ServiceGoTSRPCClient{ + URL: url, + EndPoint: endpoint, + } +} + +func (c *ServiceGoTSRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { + args := []interface{}{person} + reply := []interface{}{&addr, &e} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "ExtractAddress", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { + args := []interface{}{} + reply := []interface{}{&amount, &wahr, &hier} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "GiveMeAScalar", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { + args := []interface{}{name} + reply := []interface{}{&reply, &err} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Hello", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { + args := []interface{}{} + reply := []interface{}{&retNest_0} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Nest", args, reply) + return +} + +func (c *ServiceGoTSRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { + args := []interface{}{} + reply := []interface{}{&retTestScalarInPlace_0} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "TestScalarInPlace", args, reply) + return +} From a358cf3f034a5fe378b5902c817c97c01cd3ae4c Mon Sep 17 00:00:00 2001 From: franklin Date: Sat, 17 Dec 2016 15:55:32 +0100 Subject: [PATCH 12/23] fixed types --- demo/gorpc.go | 6 +++--- go.go | 6 +++--- gorpc.go | 2 +- prometheus/prometheus.go | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/demo/gorpc.go b/demo/gorpc.go index 7275a8a..5c05246 100644 --- a/demo/gorpc.go +++ b/demo/gorpc.go @@ -17,7 +17,7 @@ type ( ServiceGoRPCProxy struct { server *gorpc.Server service *Service - callStatsHandler *gotsrpc.GoRPCCallStatsHandlerFun + callStatsHandler gotsrpc.GoRPCCallStatsHandlerFun } ExtractAddressRequest struct { @@ -92,7 +92,7 @@ func (p *ServiceGoRPCProxy) Stop() { p.server.Stop() } -func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler *gotsrpc.GoRPCCallStatsHandlerFun) { +func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { p.callStatsHandler = handler } @@ -126,7 +126,7 @@ func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (res } if p.callStatsHandler != nil { - *p.callStatsHandler(gotsrpc.CallStats{ + p.callStatsHandler(&gotsrpc.CallStats{ Func: funcName, Package: "github.com/foomo/gotsrpc/demo", Service: "Service", diff --git a/go.go b/go.go index f358d66..4bb6517 100644 --- a/go.go +++ b/go.go @@ -442,7 +442,7 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin ` + proxyName + ` struct { server *gorpc.Server service *` + service.Name + ` - callStatsHandler *gotsrpc.GoRPCCallStatsHandlerFun + callStatsHandler gotsrpc.GoRPCCallStatsHandlerFun } `) // Request & Response types @@ -498,7 +498,7 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin p.server.Stop() } - func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler *gotsrpc.GoRPCCallStatsHandlerFun) { + func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { p.callStatsHandler = handler } `) @@ -543,7 +543,7 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin g.l(`}`) g.nl() g.l(`if p.callStatsHandler != nil {`) - g.l(`*p.callStatsHandler(gotsrpc.CallStats{`) + g.l(`p.callStatsHandler(&gotsrpc.CallStats{`) g.l(`Func: funcName,`) g.l(`Package: "` + fullPackageName + `",`) g.l(`Service: "` + service.Name + `",`) diff --git a/gorpc.go b/gorpc.go index 9cd4356..e1db980 100644 --- a/gorpc.go +++ b/gorpc.go @@ -1,4 +1,4 @@ package gotsrpc -type GoRPCCallStatsHandlerFun func(stats CallStats) \ No newline at end of file +type GoRPCCallStatsHandlerFun func(stats *CallStats) \ No newline at end of file diff --git a/prometheus/prometheus.go b/prometheus/prometheus.go index b246674..a12a7bb 100644 --- a/prometheus/prometheus.go +++ b/prometheus/prometheus.go @@ -28,7 +28,7 @@ func InstrumentService(s http.HandlerFunc) (handler http.HandlerFunc) { } } -func InstrumentGoRPCService() { +func InstrumentGoRPCService() gotsrpc.GoRPCCallStatsHandlerFun { callsCounter := p.NewSummaryVec(p.SummaryOpts{ Namespace: "gorpc", Subsystem: "service", From d4a086d569ae26f1df19c49aa28c429a3be7b62e Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 21 Dec 2016 14:46:11 +0100 Subject: [PATCH 13/23] refactored config to configure which services should be generated through gorpc --- build.go | 16 +++-- cmd/demo/demo.go | 2 +- config/config.go | 30 ++++++-- demo/config.yml | 8 ++- demo/demo.go | 10 +-- demo/{complex.go => demo_complex.go} | 12 ++-- demo/gorpc.go | 103 ++++++++++++++++++++++++--- demo/gorpcclient.go | 65 ++++++++++++++--- demo/gotsrpc.go | 92 +++++++++++++++++++++--- demo/gotsrpcclient.go | 50 ++++++++++--- go.go | 85 ++++++++++++++++------ typescript.go | 13 ++-- 12 files changed, 397 insertions(+), 89 deletions(-) rename demo/{complex.go => demo_complex.go} (75%) diff --git a/build.go b/build.go index ef31b54..0172d96 100644 --- a/build.go +++ b/build.go @@ -55,9 +55,10 @@ func Build(conf *config.Config, goPath string) { mappedTypeScript := map[string]map[string]*code{} for name, target := range conf.Targets { fmt.Fprintln(os.Stderr, "building target", name) + longPackageName := target.Package longPackageNameParts := strings.Split(longPackageName, "/") - goRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gorpc.go") + goRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gorpc.go") goRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gorpcclient.go") goTSRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gotsrpc.go") goTSRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gotsrpcclient.go") @@ -83,7 +84,7 @@ func Build(conf *config.Config, goPath string) { os.Exit(2) } - ts, err := RenderTypeScriptServices(conf.ModuleKind, services, conf.Mappings, scalarTypes, target.TypeScriptModule) + ts, err := RenderTypeScriptServices(conf.ModuleKind, services, conf.Mappings, scalarTypes, target) if err != nil { fmt.Fprintln(os.Stderr, " could not generate ts code", err) os.Exit(3) @@ -122,29 +123,29 @@ func Build(conf *config.Config, goPath string) { } } - goTSRPCProxiesCode, goerr := RenderGoTSRPCProxies(services, longPackageName, packageName) + goTSRPCProxiesCode, goerr := RenderGoTSRPCProxies(services, longPackageName, packageName, target) if goerr != nil { fmt.Fprintln(os.Stderr, " could not generate go ts rpc proxies code in target", name, goerr) os.Exit(4) } formatAndWrite(goTSRPCProxiesCode, goTSRPCProxiesFilename) - goTSRPCClientsCode, goerr := RenderGoTSRPCClients(services, longPackageName, packageName) + goTSRPCClientsCode, goerr := RenderGoTSRPCClients(services, longPackageName, packageName, target) if goerr != nil { fmt.Fprintln(os.Stderr, " could not generate go ts rpc clients code in target", name, goerr) os.Exit(4) } formatAndWrite(goTSRPCClientsCode, goTSRPCClientsFilename) - if target.RPC == true { - goRPCProxiesCode, goerr := RenderGoRPCProxies(services, longPackageName, packageName) + if len(target.GoRPC) > 0 { + goRPCProxiesCode, goerr := RenderGoRPCProxies(services, longPackageName, packageName, target) if goerr != nil { fmt.Fprintln(os.Stderr, " could not generate go rpc proxies code in target", name, goerr) os.Exit(4) } formatAndWrite(goRPCProxiesCode, goRPCProxiesFilename) - goRPCClientsCode, goerr := RenderGoRPCClients(services, longPackageName, packageName) + goRPCClientsCode, goerr := RenderGoRPCClients(services, longPackageName, packageName, target) if goerr != nil { fmt.Fprintln(os.Stderr, " could not generate go rpc clients code in target", name, goerr) os.Exit(4) @@ -152,6 +153,7 @@ func Build(conf *config.Config, goPath string) { formatAndWrite(goRPCClientsCode, goRPCClientsFilename) } } + // spew.Dump(mappedTypeScript) for goPackage, mappedStructsMap := range mappedTypeScript { mapping, ok := conf.Mappings[goPackage] diff --git a/cmd/demo/demo.go b/cmd/demo/demo.go index f81e316..e7bb9ac 100644 --- a/cmd/demo/demo.go +++ b/cmd/demo/demo.go @@ -39,7 +39,7 @@ func serveFile(name string, w http.ResponseWriter) { func main() { d := &Demo{ - proxy: demo.NewServiceGoTSRPCProxy(&demo.Service{}, "/service"), + proxy: demo.NewServiceGoTSRPCProxy(&demo.Demo{}, "/service"), } fmt.Println(http.ListenAndServe(":8080", d)) } diff --git a/config/config.go b/config/config.go index 8e94952..b6e5734 100644 --- a/config/config.go +++ b/config/config.go @@ -9,11 +9,33 @@ import ( ) type Target struct { - Package string `yaml:"package"` + Package string `yaml:"package"` Services map[string]string `yaml:"services"` - TypeScriptModule string `yaml:"module"` - Out string `yaml:"out"` - RPC bool `yaml:"rpc"` + TypeScriptModule string `yaml:"module"` + Out string `yaml:"out"` + GoRPC []string `yaml:"gorpc"` + TSRPC []string `yaml:"tsrpc"` +} + +func (t *Target) IsGoRPC(service string) bool { + for _, value := range t.GoRPC { + if value == service { + return true + } + } + return false +} + +func (t *Target) IsTSRPC(service string) bool { + if len(t.TSRPC) == 0 { + return true + } + for _, value := range t.TSRPC { + if value == service { + return true + } + } + return false } type Mapping struct { diff --git a/demo/config.yml b/demo/config.yml index 9778152..8bacaf5 100644 --- a/demo/config.yml +++ b/demo/config.yml @@ -3,10 +3,14 @@ targets: demo: module: GoTSRPC.Demo services: - /service/demo: Service + /service/foo: Foo + /service/demo: Demo package: github.com/foomo/gotsrpc/demo out: /tmp/test.ts - rpc: true + gorpc: + - Foo + - Demo + mappings: github.com/foomo/gotsrpc/demo: module: GoTSRPC.Demo diff --git a/demo/demo.go b/demo/demo.go index 369b92c..16714f0 100644 --- a/demo/demo.go +++ b/demo/demo.go @@ -6,21 +6,17 @@ type Err struct { type ScalarInPlace string -type Service struct { +type Demo struct { Bla bool } -func (s *Service) Hello(name string) (reply string, err *Err) { +func (d *Demo) Hello(name string) (reply string, err *Err) { if name == "Peter" { return "", &Err{"fuck you Peter I do not like you"} } return "Hello from the server: " + name, nil } -func sepp(bar bool) string { - return "ich bin der sepp" -} - -func (s *Service) nothingInNothinOut() { +func (d *Demo) nothingInNothinOut() { } diff --git a/demo/complex.go b/demo/demo_complex.go similarity index 75% rename from demo/complex.go rename to demo/demo_complex.go index 278593f..44fb73e 100644 --- a/demo/complex.go +++ b/demo/demo_complex.go @@ -30,22 +30,26 @@ type Person struct { iAmPrivate string } -func (s *Service) ExtractAddress(person *Person) (addr *Address, e *Err) { +func (d *Demo) ExtractAddress(person *Person) (addr *Address, e *Err) { if person.AddressPtr != nil { return person.AddressPtr, nil } return nil, &Err{"there is no address on that person"} } -func (s *Service) TestScalarInPlace() ScalarInPlace { +func (d *Demo) TestScalarInPlace() ScalarInPlace { return ScalarInPlace("hier") } -func (s *Service) Nest() *nstd.Nested { +func (d *Demo) MapCrap() (crap map[string][]int) { + return map[string][]int{} +} + +func (d *Demo) Nest() *nstd.Nested { return nil } -func (s *Service) GiveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { +func (d *Demo) GiveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { //func (s *Service) giveMeAScalar() (amount nstd.Amount, wahr nstd.True, hier ScalarInPlace) { return nstd.Amount(10), nstd.ItIsTrue, ScalarInPlace("hier") } diff --git a/demo/gorpc.go b/demo/gorpc.go index 5c05246..1e8d6f6 100644 --- a/demo/gorpc.go +++ b/demo/gorpc.go @@ -14,9 +14,83 @@ import ( ) type ( - ServiceGoRPCProxy struct { + FooGoRPCProxy struct { server *gorpc.Server - service *Service + service *Foo + callStatsHandler gotsrpc.GoRPCCallStatsHandlerFun + } + + HelloRequest struct { + Number int64 + } + HelloResponse struct { + RetHello_0 int + } +) + +func init() { + gob.Register(HelloRequest{}) + gob.Register(HelloResponse{}) +} + +func NewFooGoRPCProxy(addr string, service *Foo, tlsConfig *tls.Config) *FooGoRPCProxy { + proxy := &FooGoRPCProxy{ + service: service, + } + + if tlsConfig != nil { + proxy.server = gorpc.NewTLSServer(addr, proxy.handler, tlsConfig) + } else { + proxy.server = gorpc.NewTCPServer(addr, proxy.handler) + } + + return proxy +} + +func (p *FooGoRPCProxy) Start() error { + return p.server.Start() +} + +func (p *FooGoRPCProxy) Stop() { + p.server.Stop() +} + +func (p *FooGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { + p.callStatsHandler = handler +} + +func (p *FooGoRPCProxy) handler(clientAddr string, request interface{}) (response interface{}) { + start := time.Now() + + reqType := reflect.TypeOf(request).String() + funcNameParts := strings.Split(reqType, ".") + funcName := funcNameParts[len(funcNameParts)-1] + + switch funcName { + case "HelloRequest": + req := request.(HelloRequest) + retHello_0 := p.service.Hello(req.Number) + response = HelloResponse{RetHello_0: retHello_0} + default: + fmt.Println("Unkown request type", reflect.TypeOf(request).String()) + } + + if p.callStatsHandler != nil { + p.callStatsHandler(&gotsrpc.CallStats{ + Func: funcName, + Package: "github.com/foomo/gotsrpc/demo", + Service: "Foo", + Execution: time.Since(start), + }) + } + + return +} + +type ( + DemoGoRPCProxy struct { + server *gorpc.Server + service *Demo callStatsHandler gotsrpc.GoRPCCallStatsHandlerFun } @@ -44,6 +118,12 @@ type ( Err *Err } + MapCrapRequest struct { + } + MapCrapResponse struct { + Crap map[string][]int + } + NestRequest struct { } NestResponse struct { @@ -64,14 +144,16 @@ func init() { gob.Register(GiveMeAScalarResponse{}) gob.Register(HelloRequest{}) gob.Register(HelloResponse{}) + gob.Register(MapCrapRequest{}) + gob.Register(MapCrapResponse{}) gob.Register(NestRequest{}) gob.Register(NestResponse{}) gob.Register(TestScalarInPlaceRequest{}) gob.Register(TestScalarInPlaceResponse{}) } -func NewServiceGoRPCProxy(addr string, service *Service, tlsConfig *tls.Config) *ServiceGoRPCProxy { - proxy := &ServiceGoRPCProxy{ +func NewDemoGoRPCProxy(addr string, service *Demo, tlsConfig *tls.Config) *DemoGoRPCProxy { + proxy := &DemoGoRPCProxy{ service: service, } @@ -84,19 +166,19 @@ func NewServiceGoRPCProxy(addr string, service *Service, tlsConfig *tls.Config) return proxy } -func (p *ServiceGoRPCProxy) Start() error { +func (p *DemoGoRPCProxy) Start() error { return p.server.Start() } -func (p *ServiceGoRPCProxy) Stop() { +func (p *DemoGoRPCProxy) Stop() { p.server.Stop() } -func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { +func (p *DemoGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { p.callStatsHandler = handler } -func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (response interface{}) { +func (p *DemoGoRPCProxy) handler(clientAddr string, request interface{}) (response interface{}) { start := time.Now() reqType := reflect.TypeOf(request).String() @@ -115,6 +197,9 @@ func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (res req := request.(HelloRequest) reply, err := p.service.Hello(req.Name) response = HelloResponse{Reply: reply, Err: err} + case "MapCrapRequest": + crap := p.service.MapCrap() + response = MapCrapResponse{Crap: crap} case "NestRequest": retNest_0 := p.service.Nest() response = NestResponse{RetNest_0: retNest_0} @@ -129,7 +214,7 @@ func (p *ServiceGoRPCProxy) handler(clientAddr string, request interface{}) (res p.callStatsHandler(&gotsrpc.CallStats{ Func: funcName, Package: "github.com/foomo/gotsrpc/demo", - Service: "Service", + Service: "Demo", Execution: time.Since(start), }) } diff --git a/demo/gorpcclient.go b/demo/gorpcclient.go index a8a2001..09118a6 100644 --- a/demo/gorpcclient.go +++ b/demo/gorpcclient.go @@ -7,12 +7,12 @@ import ( gorpc "github.com/valyala/gorpc" ) -type ServiceGoRPCClient struct { +type FooGoRPCClient struct { client *gorpc.Client } -func NewServiceGoRPCClient(addr string, tlsConfig *tls.Config) *ServiceGoRPCClient { - client := &ServiceGoRPCClient{} +func NewFooGoRPCClient(addr string, tlsConfig *tls.Config) *FooGoRPCClient { + client := &FooGoRPCClient{} if tlsConfig == nil { client.client = gorpc.NewTCPClient(addr) } else { @@ -22,15 +22,49 @@ func NewServiceGoRPCClient(addr string, tlsConfig *tls.Config) *ServiceGoRPCClie return client } -func (c *ServiceGoRPCClient) Start() { +func (c *FooGoRPCClient) Start() { c.client.Start() } -func (c *ServiceGoRPCClient) Stop() { +func (c *FooGoRPCClient) Stop() { c.client.Stop() } -func (c *ServiceGoRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { +func (c *FooGoRPCClient) Hello(number int64) (retHello_0 int, clientErr error) { + req := HelloRequest{Number: number} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(HelloResponse) + return response.RetHello_0, nil +} + +type DemoGoRPCClient struct { + client *gorpc.Client +} + +func NewDemoGoRPCClient(addr string, tlsConfig *tls.Config) *DemoGoRPCClient { + client := &DemoGoRPCClient{} + if tlsConfig == nil { + client.client = gorpc.NewTCPClient(addr) + } else { + client.client = gorpc.NewTLSClient(addr, tlsConfig) + } + client.Start() + return client +} + +func (c *DemoGoRPCClient) Start() { + c.client.Start() +} + +func (c *DemoGoRPCClient) Stop() { + c.client.Stop() +} + +func (c *DemoGoRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { req := ExtractAddressRequest{Person: person} res, err := c.client.Call(req) if err != nil { @@ -41,7 +75,7 @@ func (c *ServiceGoRPCClient) ExtractAddress(person *Person) (addr *Address, e *E return response.Addr, response.E, nil } -func (c *ServiceGoRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { +func (c *DemoGoRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { req := GiveMeAScalarRequest{} res, err := c.client.Call(req) if err != nil { @@ -52,7 +86,7 @@ func (c *ServiceGoRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested. return response.Amount, response.Wahr, response.Hier, nil } -func (c *ServiceGoRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { +func (c *DemoGoRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { req := HelloRequest{Name: name} res, err := c.client.Call(req) if err != nil { @@ -63,7 +97,18 @@ func (c *ServiceGoRPCClient) Hello(name string) (reply string, err *Err, clientE return response.Reply, response.Err, nil } -func (c *ServiceGoRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { +func (c *DemoGoRPCClient) MapCrap() (crap map[string][]int, clientErr error) { + req := MapCrapRequest{} + res, err := c.client.Call(req) + if err != nil { + clientErr = err + return + } + response := res.(MapCrapResponse) + return response.Crap, nil +} + +func (c *DemoGoRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { req := NestRequest{} res, err := c.client.Call(req) if err != nil { @@ -74,7 +119,7 @@ func (c *ServiceGoRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) return response.RetNest_0, nil } -func (c *ServiceGoRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { +func (c *DemoGoRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { req := TestScalarInPlaceRequest{} res, err := c.client.Call(req) if err != nil { diff --git a/demo/gotsrpc.go b/demo/gotsrpc.go index d6e6e3f..803fc89 100644 --- a/demo/gotsrpc.go +++ b/demo/gotsrpc.go @@ -7,22 +7,22 @@ import ( time "time" ) -type ServiceGoTSRPCProxy struct { +type FooGoTSRPCProxy struct { EndPoint string allowOrigin []string - service *Service + service *Foo } -func NewDefaultServiceGoTSRPCProxy(service *Service, allowOrigin []string) *ServiceGoTSRPCProxy { - return &ServiceGoTSRPCProxy{ - EndPoint: "/service/demo", +func NewDefaultFooGoTSRPCProxy(service *Foo, allowOrigin []string) *FooGoTSRPCProxy { + return &FooGoTSRPCProxy{ + EndPoint: "/service/foo", allowOrigin: allowOrigin, service: service, } } -func NewServiceGoTSRPCProxy(service *Service, endpoint string, allowOrigin []string) *ServiceGoTSRPCProxy { - return &ServiceGoTSRPCProxy{ +func NewFooGoTSRPCProxy(service *Foo, endpoint string, allowOrigin []string) *FooGoTSRPCProxy { + return &FooGoTSRPCProxy{ EndPoint: endpoint, allowOrigin: allowOrigin, service: service, @@ -30,7 +30,7 @@ func NewServiceGoTSRPCProxy(service *Service, endpoint string, allowOrigin []str } // ServeHTTP exposes your service -func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { +func (p *FooGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { for _, origin := range p.allowOrigin { // todo we have to compare this with the referer ... and only send one @@ -51,7 +51,73 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) if callStats != nil { callStats.Func = funcName callStats.Package = "github.com/foomo/gotsrpc/demo" - callStats.Service = "Service" + callStats.Service = "Foo" + } + switch funcName { + case "Hello": + args = []interface{}{int64(0)} + err := gotsrpc.LoadArgs(args, callStats, r) + if err != nil { + gotsrpc.ErrorCouldNotLoadArgs(w) + return + } + executionStart := time.Now() + helloRet := p.service.Hello(int64(args[0].(float64))) + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{helloRet}, callStats, r, w) + return + default: + http.Error(w, "404 - not found "+r.URL.Path, http.StatusNotFound) + } +} + +type DemoGoTSRPCProxy struct { + EndPoint string + allowOrigin []string + service *Demo +} + +func NewDefaultDemoGoTSRPCProxy(service *Demo, allowOrigin []string) *DemoGoTSRPCProxy { + return &DemoGoTSRPCProxy{ + EndPoint: "/service/demo", + allowOrigin: allowOrigin, + service: service, + } +} + +func NewDemoGoTSRPCProxy(service *Demo, endpoint string, allowOrigin []string) *DemoGoTSRPCProxy { + return &DemoGoTSRPCProxy{ + EndPoint: endpoint, + allowOrigin: allowOrigin, + service: service, + } +} + +// ServeHTTP exposes your service +func (p *DemoGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + for _, origin := range p.allowOrigin { + // todo we have to compare this with the referer ... and only send one + w.Header().Add("Access-Control-Allow-Origin", origin) + } + w.Header().Set("Access-Control-Allow-Credentials", "true") + if r.Method != http.MethodPost { + if r.Method == http.MethodOptions { + return + } + gotsrpc.ErrorMethodNotAllowed(w) + return + } + + var args []interface{} + funcName := gotsrpc.GetCalledFunc(r, p.EndPoint) + callStats := gotsrpc.GetStatsForRequest(r) + if callStats != nil { + callStats.Func = funcName + callStats.Package = "github.com/foomo/gotsrpc/demo" + callStats.Service = "Demo" } switch funcName { case "ExtractAddress": @@ -90,6 +156,14 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) } gotsrpc.Reply([]interface{}{helloReply, helloErr}, callStats, r, w) return + case "MapCrap": + executionStart := time.Now() + mapCrapCrap := p.service.MapCrap() + if callStats != nil { + callStats.Execution = time.Now().Sub(executionStart) + } + gotsrpc.Reply([]interface{}{mapCrapCrap}, callStats, r, w) + return case "Nest": executionStart := time.Now() nestRet := p.service.Nest() diff --git a/demo/gotsrpcclient.go b/demo/gotsrpcclient.go index dfa5ed2..e4afe3a 100644 --- a/demo/gotsrpcclient.go +++ b/demo/gotsrpcclient.go @@ -6,51 +6,81 @@ import ( nested "github.com/foomo/gotsrpc/demo/nested" ) -type ServiceGoTSRPCClient struct { +type FooGoTSRPCClient struct { URL string EndPoint string } -func NewDefaultServiceGoTSRPCClient(url string) *ServiceGoTSRPCClient { - return NewServiceGoTSRPCClient(url, "/service/demo") +func NewDefaultFooGoTSRPCClient(url string) *FooGoTSRPCClient { + return NewFooGoTSRPCClient(url, "/service/foo") } -func NewServiceGoTSRPCClient(url string, endpoint string) *ServiceGoTSRPCClient { - return &ServiceGoTSRPCClient{ +func NewFooGoTSRPCClient(url string, endpoint string) *FooGoTSRPCClient { + return &FooGoTSRPCClient{ URL: url, EndPoint: endpoint, } } -func (c *ServiceGoTSRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { +func (c *FooGoTSRPCClient) Hello(number int64) (retHello_0 int, clientErr error) { + args := []interface{}{number} + reply := []interface{}{&retHello_0} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Hello", args, reply) + return +} + +type DemoGoTSRPCClient struct { + URL string + EndPoint string +} + +func NewDefaultDemoGoTSRPCClient(url string) *DemoGoTSRPCClient { + return NewDemoGoTSRPCClient(url, "/service/demo") +} + +func NewDemoGoTSRPCClient(url string, endpoint string) *DemoGoTSRPCClient { + return &DemoGoTSRPCClient{ + URL: url, + EndPoint: endpoint, + } +} + +func (c *DemoGoTSRPCClient) ExtractAddress(person *Person) (addr *Address, e *Err, clientErr error) { args := []interface{}{person} reply := []interface{}{&addr, &e} clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "ExtractAddress", args, reply) return } -func (c *ServiceGoTSRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { +func (c *DemoGoTSRPCClient) GiveMeAScalar() (amount nested.Amount, wahr nested.True, hier ScalarInPlace, clientErr error) { args := []interface{}{} reply := []interface{}{&amount, &wahr, &hier} clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "GiveMeAScalar", args, reply) return } -func (c *ServiceGoTSRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { +func (c *DemoGoTSRPCClient) Hello(name string) (reply string, err *Err, clientErr error) { args := []interface{}{name} reply := []interface{}{&reply, &err} clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Hello", args, reply) return } -func (c *ServiceGoTSRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { +func (c *DemoGoTSRPCClient) MapCrap() (crap map[string][]int, clientErr error) { + args := []interface{}{} + reply := []interface{}{&crap} + clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "MapCrap", args, reply) + return +} + +func (c *DemoGoTSRPCClient) Nest() (retNest_0 *nested.Nested, clientErr error) { args := []interface{}{} reply := []interface{}{&retNest_0} clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "Nest", args, reply) return } -func (c *ServiceGoTSRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { +func (c *DemoGoTSRPCClient) TestScalarInPlace() (retTestScalarInPlace_0 ScalarInPlace, clientErr error) { args := []interface{}{} reply := []interface{}{&retTestScalarInPlace_0} clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "TestScalarInPlace", args, reply) diff --git a/go.go b/go.go index 4bb6517..0919c58 100644 --- a/go.go +++ b/go.go @@ -3,6 +3,8 @@ package gotsrpc import ( "fmt" "strings" + + "github.com/foomo/gotsrpc/config" ) func (v *Value) isHTTPResponseWriter() bool { @@ -27,6 +29,8 @@ func (v *Value) goType(aliases map[string]string, packageName string) (t string) t += aliases[v.StructType.Package] + "." } t += v.StructType.Name + case v.Map != nil: + t += `map[` + v.Map.KeyType + `]` + v.Map.Value.goType(aliases, packageName) case v.Scalar != nil: // TODO this is a hack to retrieve string types if packageName != v.Scalar.Package { @@ -35,6 +39,7 @@ func (v *Value) goType(aliases map[string]string, packageName string) (t string) t += v.Scalar.Name[len(v.Scalar.Package)+1:] default: // TODO + fmt.Println("WARN: can't resolve goType") } return @@ -124,7 +129,6 @@ func strfirst(str string, strfunc func(string) string) string { } return res - } func extractImports(fields []*Field, fullPackageName string, aliases map[string]string) { @@ -159,15 +163,20 @@ func extractImports(fields []*Field, fullPackageName string, aliases map[string] } } -func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, g *code) error { +func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ "time": "time", "net/http": "http", "github.com/foomo/gotsrpc": "gotsrpc", } - for _, s := range services { - for _, m := range s.Methods { + for _, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !config.IsTSRPC(service.Name) { + continue + } + for _, m := range service.Methods { extractImports(m.Args, fullPackageName, aliases) } } @@ -185,6 +194,12 @@ func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName str ) `) for endpoint, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !config.IsTSRPC(service.Name) { + continue + } + proxyName := service.Name + "GoTSRPCProxy" g.l(` type ` + proxyName + ` struct { @@ -329,13 +344,18 @@ func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName str return nil } -func renderTSRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, g *code) error { +func renderTSRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ "github.com/foomo/gotsrpc": "gotsrpc", } - for _, s := range services { - for _, m := range s.Methods { + for _, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !config.IsTSRPC(service.Name) { + continue + } + for _, m := range service.Methods { extractImports(m.Args, fullPackageName, aliases) extractImports(m.Return, fullPackageName, aliases) } @@ -354,6 +374,12 @@ func renderTSRPCServiceClients(services map[string]*Service, fullPackageName str ) `) for endpoint, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !config.IsTSRPC(service.Name) { + continue + } + clientName := service.Name + "GoTSRPCClient" g.l(` type ` + clientName + ` struct { @@ -402,7 +428,7 @@ func renderTSRPCServiceClients(services map[string]*Service, fullPackageName str return nil } -func renderRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, g *code) error { +func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ "fmt": "fmt", "time": "time", @@ -414,8 +440,12 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin "github.com/foomo/gotsrpc": "gotsrpc", } - for _, s := range services { - for _, m := range s.Methods { + for _, service := range services { + if !config.IsGoRPC(service.Name) { + continue + } + + for _, m := range service.Methods { extractImports(m.Args, fullPackageName, aliases) extractImports(m.Return, fullPackageName, aliases) } @@ -433,7 +463,12 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin ` + imports + ` ) `) + for _, service := range services { + if !config.IsGoRPC(service.Name) { + continue + } + proxyName := service.Name + "GoRPCProxy" // Types g.l(`type (`) @@ -498,7 +533,7 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin p.server.Stop() } - func (p *ServiceGoRPCProxy) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { + func (p *` + proxyName + `) SetCallStatsHandler(handler gotsrpc.GoRPCCallStatsHandlerFun) { p.callStatsHandler = handler } `) @@ -557,14 +592,17 @@ func renderRPCServiceProxies(services map[string]*Service, fullPackageName strin return nil } -func renderRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, g *code) error { +func renderGoRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ "crypto/tls": "tls", "github.com/valyala/gorpc": "gorpc", } - for _, s := range services { - for _, m := range s.Methods { + for _, service := range services { + if !config.IsGoRPC(service.Name) { + continue + } + for _, m := range service.Methods { extractImports(m.Args, fullPackageName, aliases) extractImports(m.Return, fullPackageName, aliases) } @@ -583,6 +621,9 @@ func renderRPCServiceClients(services map[string]*Service, fullPackageName strin ) `) for _, service := range services { + if !config.IsGoRPC(service.Name) { + continue + } clientName := service.Name + "GoRPCClient" // Client type g.l(` @@ -655,9 +696,9 @@ func renderRPCServiceClients(services map[string]*Service, fullPackageName strin return nil } -func RenderGoTSRPCProxies(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { +func RenderGoTSRPCProxies(services map[string]*Service, longPackageName, packageName string, config *config.Target) (gocode string, err error) { g := newCode(" ") - err = renderTSRPCServiceProxies(services, longPackageName, packageName, g) + err = renderTSRPCServiceProxies(services, longPackageName, packageName, config, g) if err != nil { return } @@ -665,9 +706,9 @@ func RenderGoTSRPCProxies(services map[string]*Service, longPackageName, package return } -func RenderGoTSRPCClients(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { +func RenderGoTSRPCClients(services map[string]*Service, longPackageName, packageName string, config *config.Target) (gocode string, err error) { g := newCode(" ") - err = renderTSRPCServiceClients(services, longPackageName, packageName, g) + err = renderTSRPCServiceClients(services, longPackageName, packageName, config, g) if err != nil { return } @@ -675,9 +716,9 @@ func RenderGoTSRPCClients(services map[string]*Service, longPackageName, package return } -func RenderGoRPCProxies(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { +func RenderGoRPCProxies(services map[string]*Service, longPackageName, packageName string, config *config.Target) (gocode string, err error) { g := newCode(" ") - err = renderRPCServiceProxies(services, longPackageName, packageName, g) + err = renderGoRPCServiceProxies(services, longPackageName, packageName, config, g) if err != nil { return } @@ -685,9 +726,9 @@ func RenderGoRPCProxies(services map[string]*Service, longPackageName, packageNa return } -func RenderGoRPCClients(services map[string]*Service, longPackageName, packageName string) (gocode string, err error) { +func RenderGoRPCClients(services map[string]*Service, longPackageName, packageName string, config *config.Target) (gocode string, err error) { g := newCode(" ") - err = renderRPCServiceClients(services, longPackageName, packageName, g) + err = renderGoRPCServiceClients(services, longPackageName, packageName, config, g) if err != nil { return } diff --git a/typescript.go b/typescript.go index 15635cf..804984f 100644 --- a/typescript.go +++ b/typescript.go @@ -285,7 +285,7 @@ func ucFirst(str string) string { return constPrefix } -func RenderTypeScriptServices(moduleKind config.ModuleKind, services map[string]*Service, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, tsModuleName string) (typeScript string, err error) { +func RenderTypeScriptServices(moduleKind config.ModuleKind, services map[string]*Service, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, target *config.Target) (typeScript string, err error) { ts := newCode(" ") if !SkipGoTSRPC { @@ -299,7 +299,7 @@ func RenderTypeScriptServices(moduleKind config.ModuleKind, services map[string] request.open('POST', endPoint + "/" + encodeURIComponent(method), true); // this causes problems, when the browser decides to do a cors OPTIONS request // request.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); - request.send(JSON.stringify(args)); + request.send(JSON.stringify(args)); request.onload = function() { if (request.status == 200) { try { @@ -311,7 +311,7 @@ func RenderTypeScriptServices(moduleKind config.ModuleKind, services map[string] } else { err(request); } - }; + }; request.onerror = function() { err(request); }; @@ -323,11 +323,16 @@ func RenderTypeScriptServices(moduleKind config.ModuleKind, services map[string] if !SkipGoTSRPC { ts.l("} // close") } - ts.l("module " + tsModuleName + " {") + ts.l("module " + target.TypeScriptModule + " {") ts.ind(1) } for endPoint, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !target.IsTSRPC(service.Name) { + continue + } err = renderService(SkipGoTSRPC, moduleKind, service, endPoint, mappings, scalarTypes, ts) if err != nil { return From 321c12f08b4ecc41f16693c329293a39dca23d18 Mon Sep 17 00:00:00 2001 From: Jan Halfar Date: Fri, 23 Dec 2016 09:37:12 +0100 Subject: [PATCH 14/23] made go client code generation a little safer, by prepending service names in front of request and repsonse types --- go.go | 58 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/go.go b/go.go index 0919c58..4e85a59 100644 --- a/go.go +++ b/go.go @@ -165,8 +165,8 @@ func extractImports(fields []*Field, fullPackageName string, aliases map[string] func renderTSRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ - "time": "time", - "net/http": "http", + "time": "time", + "net/http": "http", "github.com/foomo/gotsrpc": "gotsrpc", } @@ -430,12 +430,12 @@ func renderTSRPCServiceClients(services map[string]*Service, fullPackageName str func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ - "fmt": "fmt", - "time": "time", - "strings": "strings", - "reflect": "reflect", - "crypto/tls": "tls", - "encoding/gob": "gob", + "fmt": "fmt", + "time": "time", + "strings": "strings", + "reflect": "reflect", + "crypto/tls": "tls", + "encoding/gob": "gob", "github.com/valyala/gorpc": "gorpc", "github.com/foomo/gotsrpc": "gotsrpc", } @@ -483,13 +483,13 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str // Request & Response types for _, method := range service.Methods { // Request type - g.l(ucfirst(method.Name) + `Request struct {`) + g.l(ucfirst(service.Name+method.Name) + `Request struct {`) for _, a := range method.Args { g.l(ucfirst(a.Name) + ` ` + a.Value.goType(aliases, fullPackageName)) } g.l(`}`) // Response type - g.l(ucfirst(method.Name) + `Response struct {`) + g.l(ucfirst(service.Name+method.Name) + `Response struct {`) for i, r := range method.Return { name := r.Name if len(name) == 0 { @@ -505,8 +505,8 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str // Init g.l(`func init() {`) for _, method := range service.Methods { - g.l(`gob.Register(` + ucfirst(method.Name) + `Request{})`) - g.l(`gob.Register(` + ucfirst(method.Name) + `Response{})`) + g.l(`gob.Register(` + ucfirst(service.Name+method.Name) + `Request{})`) + g.l(`gob.Register(` + ucfirst(service.Name+method.Name) + `Response{})`) } g.l(`}`) // Constructor @@ -562,16 +562,16 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str rets = append(rets, name) retParams = append(retParams, ucfirst(name)+`: `+name) } - g.l(`case "` + method.Name + `Request":`) + g.l(`case "` + service.Name + method.Name + `Request":`) if len(argParams) > 0 { - g.l(`req := request.(` + method.Name + `Request)`) + g.l(`req := request.(` + service.Name + method.Name + `Request)`) } if len(rets) > 0 { - g.l(strings.Join(rets, ", ") + ` := p.service.` + method.Name + `(`+strings.Join(argParams, ", ")+`)`) + g.l(strings.Join(rets, ", ") + ` := p.service.` + method.Name + `(` + strings.Join(argParams, ", ") + `)`) } else { - g.l(`p.service.` + method.Name + `(`+strings.Join(argParams, ", ")+`)`) + g.l(`p.service.` + method.Name + `(` + strings.Join(argParams, ", ") + `)`) } - g.l(`response = ` + method.Name + `Response{`+strings.Join(retParams, ", ")+`}`) + g.l(`response = ` + service.Name + method.Name + `Response{` + strings.Join(retParams, ", ") + `}`) } g.l(`default:`) g.l(`fmt.Println("Unkown request type", reflect.TypeOf(request).String())`) @@ -594,7 +594,7 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str func renderGoRPCServiceClients(services map[string]*Service, fullPackageName string, packageName string, config *config.Target, g *code) error { aliases := map[string]string{ - "crypto/tls": "tls", + "crypto/tls": "tls", "github.com/valyala/gorpc": "gorpc", } @@ -658,8 +658,8 @@ func renderGoRPCServiceClients(services map[string]*Service, fullPackageName str args := []string{} params := []string{} for _, a := range method.Args { - args = append(args, ucfirst(a.Name) + `: ` + a.Name) - params = append(params, a.Name + " " + a.Value.goType(aliases, fullPackageName)) + args = append(args, ucfirst(a.Name)+`: `+a.Name) + params = append(params, a.Name+" "+a.Value.goType(aliases, fullPackageName)) } rets := []string{} returns := []string{} @@ -668,24 +668,24 @@ func renderGoRPCServiceClients(services map[string]*Service, fullPackageName str if len(name) == 0 { name = fmt.Sprintf("ret%s_%d", method.Name, i) } - rets = append(rets, "response." + ucfirst(name)) - returns = append(returns, name + " " + r.Value.goType(aliases, fullPackageName)) + rets = append(rets, "response."+ucfirst(name)) + returns = append(returns, name+" "+r.Value.goType(aliases, fullPackageName)) } returns = append(returns, "clientErr error") g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) - g.l(`req := ` + method.Name + `Request{` + strings.Join(args, ", ") + `}`) + g.l(`req := ` + service.Name + method.Name + `Request{` + strings.Join(args, ", ") + `}`) if len(rets) > 0 { - g.l(`res, err := c.client.Call(req)`) + g.l(`rpcCallRes, rpcCallErr := c.client.Call(req)`) } else { - g.l(`_, err := c.client.Call(req)`) + g.l(`_, rpcCallErr := c.client.Call(req)`) } - g.l(`if err != nil {`) - g.l(`clientErr = err`) + g.l(`if rpcCallErr != nil {`) + g.l(`clientErr = rpcCallErr`) g.l(`return`) g.l(`}`) if len(rets) > 0 { - g.l(`response := res.(` + method.Name + `Response)`) - g.l(`return ` + strings.Join(rets, ", ") + `, nil`) + g.l(`response := rpcCallRes.(` + service.Name + method.Name + `Response)`) + g.l(`return ` + strings.Join(rets, ", ") + `, nil`) } else { g.l(`return nil`) } From a9c02ee9890c96282f9d3920cdd46e4d4ff3ca78 Mon Sep 17 00:00:00 2001 From: franklin Date: Fri, 27 Jan 2017 16:59:03 +0100 Subject: [PATCH 15/23] added response and request size to stats --- gotsrpc.go | 10 ++++++++-- stats.go | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/gotsrpc.go b/gotsrpc.go index b0a2eb3..ffcc51b 100644 --- a/gotsrpc.go +++ b/gotsrpc.go @@ -3,6 +3,7 @@ package gotsrpc import ( "context" "encoding/json" + "io/ioutil" "errors" "fmt" "go/ast" @@ -37,13 +38,17 @@ func ErrorMethodNotAllowed(w http.ResponseWriter) { func LoadArgs(args []interface{}, callStats *CallStats, r *http.Request) error { start := time.Now() - decoder := json.NewDecoder(r.Body) - err := decoder.Decode(&args) + + body, err := ioutil.ReadAll(r.Body) if err != nil { return err } + if err := json.Unmarshal(body, &args); err != nil { + return err + } if callStats != nil { callStats.Unmarshalling = time.Now().Sub(start) + callStats.RequestSize = len(body) } return nil } @@ -71,6 +76,7 @@ func Reply(response []interface{}, stats *CallStats, r *http.Request, w http.Res return } if stats != nil { + stats.ResponseSize = len(jsonBytes) stats.Marshalling = time.Now().Sub(serializationStart) } //r = r.WithContext(ctx) diff --git a/stats.go b/stats.go index 5bead3e..de4e2ef 100644 --- a/stats.go +++ b/stats.go @@ -7,4 +7,6 @@ type CallStats struct { Execution time.Duration Marshalling time.Duration Unmarshalling time.Duration + RequestSize int + ResponseSize int } From 2c24d2c7a12c48da4c190eb208f809ce04ca2fd8 Mon Sep 17 00:00:00 2001 From: franklin Date: Fri, 27 Jan 2017 17:18:23 +0100 Subject: [PATCH 16/23] added size to instrumentation --- prometheus/prometheus.go | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/prometheus/prometheus.go b/prometheus/prometheus.go index a12a7bb..1032eba 100644 --- a/prometheus/prometheus.go +++ b/prometheus/prometheus.go @@ -8,22 +8,32 @@ import ( ) func InstrumentService(s http.HandlerFunc) (handler http.HandlerFunc) { - counterVec := p.NewSummaryVec(p.SummaryOpts{ + requestDuration := p.NewSummaryVec(p.SummaryOpts{ Namespace: "gotsrpc", Subsystem: "service", Name: "time_nanoseconds", Help: "nanoseconds to unmarshal requests, execute a service function and marshal its reponses", }, []string{"package", "service", "func", "type"}) - p.MustRegister(counterVec) + requestSize := p.NewSummaryVec(p.SummaryOpts{ + Namespace: "gotsrpc", + Subsystem: "service", + Name: "size_bytes", + Help: "request and response sizes in bytes", + }, []string{"package", "service", "func", "type"}) + + p.MustRegister(requestSize) + p.MustRegister(requestDuration) return func(w http.ResponseWriter, r *http.Request) { *r = *gotsrpc.RequestWithStatsContext(r) s.ServeHTTP(w, r) stats := gotsrpc.GetStatsForRequest(r) if stats != nil { - counterVec.WithLabelValues(stats.Package, stats.Service, stats.Func, "unmarshalling").Observe(float64(stats.Unmarshalling)) - counterVec.WithLabelValues(stats.Package, stats.Service, stats.Func, "execution").Observe(float64(stats.Execution)) - counterVec.WithLabelValues(stats.Package, stats.Service, stats.Func, "marshalling").Observe(float64(stats.Marshalling)) + requestSize.WithLabelValues(stats.Package, stats.Service, stats.Func, "request").Observe(float64(stats.RequestSize)) + requestSize.WithLabelValues(stats.Package, stats.Service, stats.Func, "response").Observe(float64(stats.ResponseSize)) + requestDuration.WithLabelValues(stats.Package, stats.Service, stats.Func, "unmarshalling").Observe(float64(stats.Unmarshalling)) + requestDuration.WithLabelValues(stats.Package, stats.Service, stats.Func, "execution").Observe(float64(stats.Execution)) + requestDuration.WithLabelValues(stats.Package, stats.Service, stats.Func, "marshalling").Observe(float64(stats.Marshalling)) } } } From ba828f4713bea486dd309b1dc30faa545164bc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20L=C3=B6ffert?= Date: Wed, 15 Feb 2017 15:27:40 +0100 Subject: [PATCH 17/23] centralized gopath handling --- build.go | 7 ++++--- gotsrpc.go | 22 ++++++++++++++++------ servicereader.go | 29 +++++++++++++---------------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/build.go b/build.go index 0172d96..edf4750 100644 --- a/build.go +++ b/build.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path" + "runtime" "sort" "strings" @@ -58,7 +59,7 @@ func Build(conf *config.Config, goPath string) { longPackageName := target.Package longPackageNameParts := strings.Split(longPackageName, "/") - goRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gorpc.go") + goRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gorpc.go") goRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gorpcclient.go") goTSRPCProxiesFilename := path.Join(goPath, "src", longPackageName, "gotsrpc.go") goTSRPCClientsFilename := path.Join(goPath, "src", longPackageName, "gotsrpcclient.go") @@ -76,8 +77,8 @@ func Build(conf *config.Config, goPath string) { remove(goTSRPCClientsFilename) packageName := longPackageNameParts[len(longPackageNameParts)-1] - - services, structs, scalarTypes, constants, err := Read(goPath, longPackageName, target.Services) + goPaths := []string{goPath, runtime.GOROOT()} + services, structs, scalarTypes, constants, err := Read(goPaths, longPackageName, target.Services) if err != nil { fmt.Fprintln(os.Stderr, " an error occured while trying to understand your code", err) diff --git a/gotsrpc.go b/gotsrpc.go index ffcc51b..9a329e0 100644 --- a/gotsrpc.go +++ b/gotsrpc.go @@ -3,12 +3,12 @@ package gotsrpc import ( "context" "encoding/json" - "io/ioutil" "errors" "fmt" "go/ast" "go/parser" "go/token" + "io/ioutil" "net/http" "path" "strings" @@ -94,12 +94,22 @@ func jsonDump(v interface{}) { fmt.Println(string(jsonBytes)) } -func parsePackage(goPath string, packageName string) (pkg *ast.Package, err error) { - fset := token.NewFileSet() - dir := path.Join(goPath, "src", packageName) - pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors) +func parseDir(goPaths []string, packageName string) (map[string]*ast.Package, error) { + for _, goPath := range goPaths { + fset := token.NewFileSet() + dir := path.Join(goPath, "src", packageName) + pkgs, err := parser.ParseDir(fset, dir, nil, parser.AllErrors) + if err == nil { + return pkgs, nil + } + } + return nil, errors.New("could not parse dir for package name: " + packageName + " in goPaths " + strings.Join(goPaths, ", ")) +} + +func parsePackage(goPaths []string, packageName string) (pkg *ast.Package, err error) { + pkgs, err := parseDir(goPaths, packageName) if err != nil { - return nil, err + return nil, errors.New("could not parse package " + packageName + ": " + err.Error()) } packageNameParts := strings.Split(packageName, "/") if len(packageNameParts) == 0 { diff --git a/servicereader.go b/servicereader.go index 9aeaea1..06414b1 100644 --- a/servicereader.go +++ b/servicereader.go @@ -6,7 +6,6 @@ import ( "go/ast" "go/token" "reflect" - "runtime" "sort" "strings" ) @@ -182,13 +181,14 @@ func loadConstants(pkg *ast.Package) map[string]*ast.BasicLit { } -func Read(goPath string, packageName string, serviceMap map[string]string) (services map[string]*Service, structs map[string]*Struct, scalars map[string]*Scalar, constants map[string]map[string]*ast.BasicLit, err error) { +func Read(goPaths []string, packageName string, serviceMap map[string]string) (services map[string]*Service, structs map[string]*Struct, scalars map[string]*Scalar, constants map[string]map[string]*ast.BasicLit, err error) { if len(serviceMap) == 0 { err = errors.New("nothing to do service names are empty") return } - pkg, err := parsePackage(goPath, packageName) - if err != nil { + pkg, parseErr := parsePackage(goPaths, packageName) + if parseErr != nil { + err = parseErr return } services, err = readServicesInPackage(pkg, packageName, serviceMap) @@ -211,7 +211,7 @@ func Read(goPath string, packageName string, serviceMap map[string]string) (serv structs = map[string]*Struct{} scalars = map[string]*Scalar{} - collectErr := collectTypes(goPath, missingTypes, structs, scalars) + collectErr := collectTypes(goPaths, missingTypes, structs, scalars) if collectErr != nil { err = errors.New("error while collecting structs: " + collectErr.Error()) } @@ -225,7 +225,7 @@ func Read(goPath string, packageName string, serviceMap map[string]string) (serv structPackage := structDef.Package _, ok := constants[structPackage] if !ok { - pkg, constPkgErr := parsePackage(goPath, structPackage) + pkg, constPkgErr := parsePackage(goPaths, structPackage) if constPkgErr != nil { err = constPkgErr return @@ -265,7 +265,7 @@ func fixFieldStructs(fields []*Field, structs map[string]*Struct, scalars map[st } } -func collectTypes(goPath string, missingTypes map[string]bool, structs map[string]*Struct, scalars map[string]*Scalar) error { +func collectTypes(goPaths []string, missingTypes map[string]bool, structs map[string]*Struct, scalars map[string]*Scalar) error { scannedPackageStructs := map[string]map[string]*Struct{} scannedPackageScalars := map[string]map[string]*Scalar{} missingTypeNames := func() []string { @@ -297,19 +297,19 @@ func collectTypes(goPath string, missingTypes map[string]bool, structs map[strin packageStructs, structOK := scannedPackageStructs[packageName] packageScalars, scalarOK := scannedPackageScalars[packageName] if !structOK || !scalarOK { - parsedPackageStructs, parsedPackageScalars, err := getTypesInPackage(goPath, packageName) + parsedPackageStructs, parsedPackageScalars, err := getTypesInPackage(goPaths, packageName) if err != nil { return err } - trace("found structs in", goPath, packageName) + trace("found structs in", goPaths, packageName) for structName, strct := range packageStructs { trace(" struct", structName, strct) if strct == nil { panic("how could that be") } } - trace("found scalars in", goPath, packageName) + trace("found scalars in", goPaths, packageName) for scalarName, scalar := range parsedPackageScalars { trace(" scalar", scalarName, scalar) } @@ -417,13 +417,10 @@ func (st *StructType) FullName() string { return fullName } -func getTypesInPackage(goPath string, packageName string) (structs map[string]*Struct, scalars map[string]*Scalar, err error) { - pkg, err := parsePackage(goPath, packageName) +func getTypesInPackage(goPaths []string, packageName string) (structs map[string]*Struct, scalars map[string]*Scalar, err error) { + pkg, err := parsePackage(goPaths, packageName) if err != nil { - pkg, err = parsePackage(runtime.GOROOT(), packageName) - if err != nil { - return nil, nil, err - } + return nil, nil, err } structs, scalars, err = readStructs(pkg, packageName) if err != nil { From 1ed5ec1bf9352b61d03c842fd22054ab2d573a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20L=C3=B6ffert?= Date: Wed, 15 Feb 2017 17:58:31 +0100 Subject: [PATCH 18/23] build tsrpc only if necessary --- build.go | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/build.go b/build.go index edf4750..d63904e 100644 --- a/build.go +++ b/build.go @@ -123,20 +123,21 @@ func Build(conf *config.Config, goPath string) { os.Exit(5) } } + if len(target.TSRPC) > 0 { + goTSRPCProxiesCode, goerr := RenderGoTSRPCProxies(services, longPackageName, packageName, target) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate go ts rpc proxies code in target", name, goerr) + os.Exit(4) + } + formatAndWrite(goTSRPCProxiesCode, goTSRPCProxiesFilename) - goTSRPCProxiesCode, goerr := RenderGoTSRPCProxies(services, longPackageName, packageName, target) - if goerr != nil { - fmt.Fprintln(os.Stderr, " could not generate go ts rpc proxies code in target", name, goerr) - os.Exit(4) + goTSRPCClientsCode, goerr := RenderGoTSRPCClients(services, longPackageName, packageName, target) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate go ts rpc clients code in target", name, goerr) + os.Exit(4) + } + formatAndWrite(goTSRPCClientsCode, goTSRPCClientsFilename) } - formatAndWrite(goTSRPCProxiesCode, goTSRPCProxiesFilename) - - goTSRPCClientsCode, goerr := RenderGoTSRPCClients(services, longPackageName, packageName, target) - if goerr != nil { - fmt.Fprintln(os.Stderr, " could not generate go ts rpc clients code in target", name, goerr) - os.Exit(4) - } - formatAndWrite(goTSRPCClientsCode, goTSRPCClientsFilename) if len(target.GoRPC) > 0 { goRPCProxiesCode, goerr := RenderGoRPCProxies(services, longPackageName, packageName, target) From eac5865cae7c2e236a90a44020ac8f5c9fdaf09b Mon Sep 17 00:00:00 2001 From: Florian Schlegel Date: Thu, 16 Feb 2017 12:05:44 +0100 Subject: [PATCH 19/23] better errors for parseDirs() --- gotsrpc.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gotsrpc.go b/gotsrpc.go index 9a329e0..b574bb5 100644 --- a/gotsrpc.go +++ b/gotsrpc.go @@ -95,6 +95,7 @@ func jsonDump(v interface{}) { } func parseDir(goPaths []string, packageName string) (map[string]*ast.Package, error) { + errorStrings := map[string]string{} for _, goPath := range goPaths { fset := token.NewFileSet() dir := path.Join(goPath, "src", packageName) @@ -102,8 +103,9 @@ func parseDir(goPaths []string, packageName string) (map[string]*ast.Package, er if err == nil { return pkgs, nil } + errorStrings[dir] = err.Error() } - return nil, errors.New("could not parse dir for package name: " + packageName + " in goPaths " + strings.Join(goPaths, ", ")) + return nil, errors.New("could not parse dir for package name: " + packageName + " in goPaths " + strings.Join(goPaths, ", ") + " : " + fmt.Sprint(errorStrings)) } func parsePackage(goPaths []string, packageName string) (pkg *ast.Package, err error) { From f4e3c7dd66f1772ef31237fdb9a6793477b8f060 Mon Sep 17 00:00:00 2001 From: Florian Schlegel Date: Thu, 16 Feb 2017 16:07:00 +0100 Subject: [PATCH 20/23] exposed Serve() for gorpc --- go.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/go.go b/go.go index 4e85a59..f2743d3 100644 --- a/go.go +++ b/go.go @@ -528,6 +528,10 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str func (p *` + proxyName + `) Start() error { return p.server.Start() } + + func (p *` + proxyName + `) Serve() error { + return p.server.Serve() + } func (p *` + proxyName + `) Stop() { p.server.Stop() From d09e43683326f30d0c260c098b1ccd32deacf0e0 Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 22 Feb 2017 08:40:05 +0100 Subject: [PATCH 21/23] added basic php client generation for ts rcp services --- build.go | 14 ++++++- config/config.go | 30 +++++++++++--- go.go | 2 +- php.go | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 9 deletions(-) create mode 100644 php.go diff --git a/build.go b/build.go index d63904e..f3e77e7 100644 --- a/build.go +++ b/build.go @@ -7,12 +7,11 @@ import ( "io/ioutil" "os" "path" + "path/filepath" "runtime" "sort" "strings" - "path/filepath" - "github.com/foomo/gotsrpc/config" ) @@ -154,6 +153,17 @@ func Build(conf *config.Config, goPath string) { } formatAndWrite(goRPCClientsCode, goRPCClientsFilename) } + + if len(target.PHPRPC) > 0 { + phpRPCClientsCode, goerr := RenderPHPRPCClients(services, target) + if goerr != nil { + fmt.Fprintln(os.Stderr, " could not generate php rpc clients code in target", name, goerr) + os.Exit(4) + } + for filename, code := range phpRPCClientsCode { + updateCode(filename, code) + } + } } // spew.Dump(mappedTypeScript) diff --git a/config/config.go b/config/config.go index b6e5734..ba74fad 100644 --- a/config/config.go +++ b/config/config.go @@ -8,13 +8,19 @@ import ( "gopkg.in/yaml.v2" ) +type PHPTarget struct { + Out string `yaml:"out"` + Namespace string `yaml:"namespace"` +} + type Target struct { - Package string `yaml:"package"` - Services map[string]string `yaml:"services"` - TypeScriptModule string `yaml:"module"` - Out string `yaml:"out"` - GoRPC []string `yaml:"gorpc"` - TSRPC []string `yaml:"tsrpc"` + Package string `yaml:"package"` + Services map[string]string `yaml:"services"` + TypeScriptModule string `yaml:"module"` + Out string `yaml:"out"` + GoRPC []string `yaml:"gorpc"` + TSRPC []string `yaml:"tsrpc"` + PHPRPC map[string]*PHPTarget `yaml:"phprpc"` } func (t *Target) IsGoRPC(service string) bool { @@ -38,6 +44,18 @@ func (t *Target) IsTSRPC(service string) bool { return false } +func (t *Target) IsPHPRPC(service string) bool { + if len(t.PHPRPC) == 0 { + return false + } + _, ok := t.PHPRPC[service] + return ok +} + +func (t *Target) GetPHPTarget(service string) *PHPTarget { + return t.PHPRPC[service] +} + type Mapping struct { GoPackage string `yaml:"-"` Out string `yaml:"out"` diff --git a/go.go b/go.go index f2743d3..6ce0a3f 100644 --- a/go.go +++ b/go.go @@ -528,7 +528,7 @@ func renderGoRPCServiceProxies(services map[string]*Service, fullPackageName str func (p *` + proxyName + `) Start() error { return p.server.Start() } - + func (p *` + proxyName + `) Serve() error { return p.server.Serve() } diff --git a/php.go b/php.go new file mode 100644 index 0000000..9f30175 --- /dev/null +++ b/php.go @@ -0,0 +1,106 @@ +package gotsrpc + +import ( + "github.com/foomo/gotsrpc/config" + "strings" +) + +func renderPHPRPCServiceClients(service *Service, namespce string, g *code) error { + g.l(` ['method' => 'POST', 'timeout' => 5, 'header' => 'Content-type: application/json']];`) + g.nl() + g.l(`private $options;`) + g.l(`private $endpoint;`) + g.nl() + + // Constructor + g.l(`public function __constructor($endpoint, $options=null)`) + g.l(`{`) + g.ind(1) + g.l(`$this->endpoint = $endpoint;`) + g.l(`$this->options = (is_null($options)) ? self::$defaultOptions : $options;`) + g.ind(-1) + g.l(`}`) + g.nl() + + // Service methods + for _, method := range service.Methods { + args := []string{} + params := []string{} + for _, a := range method.Args { + args = append(args, "$"+a.Name) + params = append(params, "$"+a.Name) + } + //rets := []string{} + //returns := []string{} + //for i, r := range method.Return { + // name := r.Name + // if len(name) == 0 { + // name = fmt.Sprintf("ret%s_%d", method.Name, i) + // } + // rets = append(rets, "&"+name) + // returns = append(returns, name+" "+r.Value.goType(aliases, fullPackageName)) + //} + //returns = append(returns, "clientErr error") + //g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) + //g.l(`args := []interface{}{` + strings.Join(args, ", ") + `}`) + //g.l(`reply := []interface{}{` + strings.Join(rets, ", ") + `}`) + //g.l(`clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) + //g.l(`return`) + //g.l(`}`) + //g.nl() + + g.l(`public function ` + lcfirst(method.Name) + `(` + strings.Join(params, ", ") + `)`) + g.l(`{`) + g.ind(1) + g.l(`return $this->call('` + method.Name + `', [` + strings.Join(params, ", ") + `]);`) + g.ind(-1) + g.l(`}`) + g.nl() + } + + // Protected methods + g.l(`protected function call($method, array $request)`) + g.l(`{`) + g.ind(1) + g.l(`$options = $this->options;`) + g.l(`$options['http']['content'] = json_encode($request);`) + g.l(`$options['http']['content'] = json_encode($request);`) + g.l(`return json_decode(file_get_contents($this->endpoint . '/' . $method, false, stream_context_create($options)));`) + g.ind(-1) + g.l(`}`) + g.nl() + + g.ind(-1) + g.l(`}`) + g.nl() + return nil +} + +func RenderPHPRPCClients(services map[string]*Service, config *config.Target) (code map[string]string, err error) { + code = map[string]string{} + for _, service := range services { + // Check if we should render this service as ts rcp + // Note: remove once there's a separate gorcp generator + if !config.IsPHPRPC(service.Name) { + continue + } + target := config.GetPHPTarget(service.Name) + g := newCode(" ") + if err = renderPHPRPCServiceClients(service, target.Namespace, g); err != nil { + return + } + code[target.Out+"/"+service.Name+"Client.php"] = g.string() + } + return +} From c78f00ba6c0521b2b27bf6078005c68f71edf42a Mon Sep 17 00:00:00 2001 From: franklin Date: Wed, 22 Feb 2017 09:04:12 +0100 Subject: [PATCH 22/23] added docs and throwing exception --- php.go | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/php.go b/php.go index 9f30175..3fa0efa 100644 --- a/php.go +++ b/php.go @@ -24,7 +24,11 @@ func renderPHPRPCServiceClients(service *Service, namespce string, g *code) erro g.nl() // Constructor - g.l(`public function __constructor($endpoint, $options=null)`) + g.l(`/**`) + g.l(` * @param string $endpoint`) + g.l(` * @param array $options`) + g.l(` */`) + g.l(`public function __construct($endpoint, array $options=null)`) g.l(`{`) g.ind(1) g.l(`$this->endpoint = $endpoint;`) @@ -37,29 +41,15 @@ func renderPHPRPCServiceClients(service *Service, namespce string, g *code) erro for _, method := range service.Methods { args := []string{} params := []string{} + + g.l(`/**`) for _, a := range method.Args { args = append(args, "$"+a.Name) params = append(params, "$"+a.Name) + g.l(` * @param $` + a.Name) } - //rets := []string{} - //returns := []string{} - //for i, r := range method.Return { - // name := r.Name - // if len(name) == 0 { - // name = fmt.Sprintf("ret%s_%d", method.Name, i) - // } - // rets = append(rets, "&"+name) - // returns = append(returns, name+" "+r.Value.goType(aliases, fullPackageName)) - //} - //returns = append(returns, "clientErr error") - //g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) - //g.l(`args := []interface{}{` + strings.Join(args, ", ") + `}`) - //g.l(`reply := []interface{}{` + strings.Join(rets, ", ") + `}`) - //g.l(`clientErr = gotsrpc.CallClient(c.URL, c.EndPoint, "` + method.Name + `", args, reply)`) - //g.l(`return`) - //g.l(`}`) - //g.nl() - + g.l(` * @return array`) + g.l(` */`) g.l(`public function ` + lcfirst(method.Name) + `(` + strings.Join(params, ", ") + `)`) g.l(`{`) g.ind(1) @@ -70,13 +60,24 @@ func renderPHPRPCServiceClients(service *Service, namespce string, g *code) erro } // Protected methods + g.l(`/**`) + g.l(` * @param string $method`) + g.l(` * @param array $request`) + g.l(` * @return array`) + g.l(` * @throws \Exception`) + g.l(` */`) g.l(`protected function call($method, array $request)`) g.l(`{`) g.ind(1) g.l(`$options = $this->options;`) g.l(`$options['http']['content'] = json_encode($request);`) - g.l(`$options['http']['content'] = json_encode($request);`) - g.l(`return json_decode(file_get_contents($this->endpoint . '/' . $method, false, stream_context_create($options)));`) + g.l(`if (false === $content = @file_get_contents($this->endpoint . '/' . $method, false, stream_context_create($options))) {`) + g.ind(1) + g.l(`$err = error_get_last();`) + g.l(`throw new \Exception($err['message'], $err['type']);`) + g.ind(-1) + g.l(`}`) + g.l(`return json_decode($content);`) g.ind(-1) g.l(`}`) g.nl() From 99830b9e5026e8d14d6d4d79df597d5a3fff5944 Mon Sep 17 00:00:00 2001 From: franklin Date: Mon, 20 Mar 2017 13:39:26 +0100 Subject: [PATCH 23/23] made gorpc client public --- go.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/go.go b/go.go index 6ce0a3f..a642a87 100644 --- a/go.go +++ b/go.go @@ -632,7 +632,7 @@ func renderGoRPCServiceClients(services map[string]*Service, fullPackageName str // Client type g.l(` type ` + clientName + ` struct { - client *gorpc.Client + Client *gorpc.Client } `) // Constructor @@ -640,20 +640,19 @@ func renderGoRPCServiceClients(services map[string]*Service, fullPackageName str func New` + clientName + `(addr string, tlsConfig *tls.Config) *` + clientName + ` { client := &` + clientName + `{} if tlsConfig == nil { - client.client = gorpc.NewTCPClient(addr) + client.Client = gorpc.NewTCPClient(addr) } else { - client.client = gorpc.NewTLSClient(addr, tlsConfig) + client.Client = gorpc.NewTLSClient(addr, tlsConfig) } - client.Start() return client } func (c *` + clientName + `) Start() { - c.client.Start() + c.Client.Start() } func (c *` + clientName + `) Stop() { - c.client.Stop() + c.Client.Stop() } `) g.nl() @@ -679,9 +678,9 @@ func renderGoRPCServiceClients(services map[string]*Service, fullPackageName str g.l(`func (c *` + clientName + `) ` + method.Name + `(` + strings.Join(params, ", ") + `) (` + strings.Join(returns, ", ") + `) {`) g.l(`req := ` + service.Name + method.Name + `Request{` + strings.Join(args, ", ") + `}`) if len(rets) > 0 { - g.l(`rpcCallRes, rpcCallErr := c.client.Call(req)`) + g.l(`rpcCallRes, rpcCallErr := c.Client.Call(req)`) } else { - g.l(`_, rpcCallErr := c.client.Call(req)`) + g.l(`_, rpcCallErr := c.Client.Call(req)`) } g.l(`if rpcCallErr != nil {`) g.l(`clientErr = rpcCallErr`)