diff --git a/cmd/gotsrpc/gotsrpc.go b/cmd/gotsrpc/gotsrpc.go index 0355cad..b133b7b 100644 --- a/cmd/gotsrpc/gotsrpc.go +++ b/cmd/gotsrpc/gotsrpc.go @@ -83,7 +83,7 @@ func main() { fmt.Fprintln(os.Stderr, "an error occured while trying to understand your code", err) os.Exit(2) } - ts, err := gotsrpc.RenderTypeScript(services, structs, target.TypeScriptModule) + ts, err := gotsrpc.RenderTypeScriptServices(services, conf.Mappings, target.TypeScriptModule) if err != nil { fmt.Fprintln(os.Stderr, "could not generate ts code", err) os.Exit(3) @@ -91,6 +91,17 @@ func main() { fmt.Println(os.Stdout, ts) + mappedCode, err := gotsrpc.RenderStructsToPackages(structs, conf.Mappings) + if err != nil { + fmt.Println("struct gen err", err) + os.Exit(4) + } + + for tsModule, code := range mappedCode { + fmt.Println("-----------------", tsModule, "--------------------") + fmt.Println(code) + } + gocode, goerr := gotsrpc.RenderGo(services, packageName) if goerr != nil { fmt.Fprintln(os.Stderr, "could not generate go code", goerr) diff --git a/config/config.go b/config/config.go index f1475a3..fcaa89d 100644 --- a/config/config.go +++ b/config/config.go @@ -19,9 +19,11 @@ type Mapping struct { TypeScriptModule string `yaml:"module"` } +type TypeScriptMappings map[string]*Mapping + type Config struct { Targets map[string]*Target - Mappings map[string]*Mapping + Mappings TypeScriptMappings } func LoadConfigFile(file string) (conf *Config, err error) { diff --git a/cmd/demo/config.yml b/demo/config.yml similarity index 63% rename from cmd/demo/config.yml rename to demo/config.yml index bdd8aef..c427acc 100644 --- a/cmd/demo/config.yml +++ b/demo/config.yml @@ -1,6 +1,7 @@ --- targets: demo: + module: GoTSRPC.Demo services: - Service package: github.com/foomo/gotsrpc/demo @@ -9,3 +10,6 @@ mappings: github.com/foomo/gotsrpc/demo: module: GoTSRPC.Demo out: /tmp/test-files.ts + github.com/foomo/gotsrpc/demo/nested: + module: GoTSRPC.Demo.Nested + out: /tmp/test-files.ts diff --git a/demo/gotsrpc.go b/demo/gotsrpc.go index 9157fa6..99765c9 100644 --- a/demo/gotsrpc.go +++ b/demo/gotsrpc.go @@ -2,30 +2,48 @@ package demo import ( - "github.com/foomo/gotsrpc" - "net/http" + gotsrpc "github.com/foomo/gotsrpc" + demo "github.com/foomo/gotsrpc/demo" + http "net/http" ) type ServiceGoTSRPCProxy struct { - EndPoint string - service *Service + EndPoint string + allowOrigin []string + service *Service } -func NewServiceGoTSRPCProxy(service *Service, endpoint string) *ServiceGoTSRPCProxy { +func NewServiceGoTSRPCProxy(service *Service, endpoint string, allowOrigin []string) *ServiceGoTSRPCProxy { return &ServiceGoTSRPCProxy{ - EndPoint: endpoint, - service: service, + EndPoint: endpoint, + allowOrigin: allowOrigin, + service: service, } } // ServeHTTP exposes your service func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { + + for _, origin := range p.allowOrigin { + w.Header().Add("Access-Control-Allow-Origin", origin) + } + w.Header().Set("Access-Control-Allow-Credentials", "true") if r.Method != "POST" { gotsrpc.ErrorMethodNotAllowed(w) return } var args []interface{} switch gotsrpc.GetCalledFunc(r, p.EndPoint) { + case "ExtractAddress": + args = []interface{}{&Person{}} + err := gotsrpc.LoadArgs(args, r) + if err != nil { + gotsrpc.ErrorCouldNotLoadArgs(w) + return + } + extractAddressAddr, extractAddressE := p.service.ExtractAddress(args[0].(*demo.Person)) + gotsrpc.Reply([]interface{}{extractAddressAddr, extractAddressE}, w) + return case "Hello": args = []interface{}{""} err := gotsrpc.LoadArgs(args, r) @@ -40,48 +58,5 @@ func (p *ServiceGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) p.service.NothingInNothinOut() gotsrpc.Reply([]interface{}{}, w) return - case "ExtractAddress": - args = []interface{}{&Person{}} - err := gotsrpc.LoadArgs(args, r) - if err != nil { - gotsrpc.ErrorCouldNotLoadArgs(w) - return - } - extractAddressAddr, extractAddressE := p.service.ExtractAddress(args[0].(*Person)) - gotsrpc.Reply([]interface{}{extractAddressAddr, extractAddressE}, w) - return - } -} - -type FooGoTSRPCProxy struct { - EndPoint string - service *Foo -} - -func NewFooGoTSRPCProxy(service *Foo, endpoint string) *FooGoTSRPCProxy { - return &FooGoTSRPCProxy{ - EndPoint: endpoint, - service: service, - } -} - -// ServeHTTP exposes your service -func (p *FooGoTSRPCProxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { - gotsrpc.ErrorMethodNotAllowed(w) - return - } - var args []interface{} - switch gotsrpc.GetCalledFunc(r, p.EndPoint) { - case "Hello": - args = []interface{}{int64(0)} - err := gotsrpc.LoadArgs(args, r) - if err != nil { - gotsrpc.ErrorCouldNotLoadArgs(w) - return - } - helloRet := p.service.Hello(args[0].(int64)) - gotsrpc.Reply([]interface{}{helloRet}, w) - return } } diff --git a/go.go b/go.go index 518c373..42de320 100644 --- a/go.go +++ b/go.go @@ -70,14 +70,6 @@ func ucfirst(str string) string { } func strfirst(str string, strfunc func(string) string) string { - /* - if len(str) == 0 { - return "" - } else if len(str) == 1 { - return strfunc(str) - } - return strfunc(str[0:1]) + str[1:] - */ res := "" for i, char := range str { if i == 0 { @@ -190,11 +182,9 @@ func renderServiceProxies(services []*Service, packageName string, g *code) erro for argI, arg := range method.Args { if argI == 0 && arg.Value.isHTTPResponseWriter() { - trace("skipping first arg is a http.ResponseWriter") continue } if argI == 1 && arg.Value.isHTTPRequest() { - trace("skipping second arg is a *http.Request") isSessionRequest = true continue } diff --git a/servicereader.go b/servicereader.go index 38a00bb..7404283 100644 --- a/servicereader.go +++ b/servicereader.go @@ -154,7 +154,6 @@ func Read(goPath string, packageName string, serviceNames []string) (services [] return } - jsonTrace(services) structTypes := map[string]*StructType{} for _, s := range services { for _, m := range s.Methods { @@ -162,7 +161,6 @@ func Read(goPath string, packageName string, serviceNames []string) (services [] collecStructTypes(m.Args, structTypes) } } - jsonTrace(structTypes) structs = map[string]*Struct{} for wantedName := range structTypes { structs[wantedName] = nil @@ -172,13 +170,13 @@ func Read(goPath string, packageName string, serviceNames []string) (services [] err = errors.New("error while collecting structs: " + collectErr.Error()) } - jsonTrace(structs) return } func collectStructs(goPath string, structs map[string]*Struct) error { scannedPackages := map[string]map[string]*Struct{} for structsPending(structs) { + trace("pending", len(structs)) for fullName, strct := range structs { if strct != nil { continue @@ -190,7 +188,7 @@ func collectStructs(goPath string, structs map[string]*Struct) error { packageName := strings.Join(fullNameParts, ".") - //trace(fullName, "==========================>", fullNameParts, "=============>", packageName) + // trace(fullName, "==========================>", fullNameParts, "=============>", packageName) packageStructs, ok := scannedPackages[packageName] if !ok { @@ -202,6 +200,7 @@ func collectStructs(goPath string, structs map[string]*Struct) error { scannedPackages[packageName] = packageStructs } for packageStructName, packageStruct := range packageStructs { + // trace("------------------------------------>", packageStructName, packageStruct) existingStruct, needed := structs[packageStructName] if needed && existingStruct == nil { structs[packageStructName] = packageStruct @@ -213,8 +212,12 @@ func collectStructs(goPath string, structs map[string]*Struct) error { } func structsPending(structs map[string]*Struct) bool { - for _, structType := range structs { - if structType == nil || !structType.DepsSatisfied(structs) { + for name, structType := range structs { + if structType == nil { + trace("missing", name) + return true + } + if !structType.DepsSatisfied(structs) { return true } } @@ -227,10 +230,11 @@ func (s *Struct) DepsSatisfied(structs map[string]*Struct) bool { if !ok { // hey there is more todo structs[fullName] = nil + trace("need work ----------------------" + fullName) return true } if strct == nil { - trace("need work " + fullName) + trace("need work ----------------------" + fullName) return true } return false @@ -249,7 +253,6 @@ func (s *Struct) DepsSatisfied(structs map[string]*Struct) bool { if needsWork(fieldStructType.FullName()) { return false } - } } return !needsWork(s.FullName()) @@ -277,7 +280,6 @@ func getStructsInPackage(goPath string, packageName string) (structs map[string] pkg, err = parsePackage(runtime.GOROOT(), packageName) if err != nil { return nil, err - } } structs, err = readStructs(pkg, packageName) diff --git a/typereader.go b/typereader.go index faff04f..97953ba 100644 --- a/typereader.go +++ b/typereader.go @@ -30,7 +30,7 @@ func trace(args ...interface{}) { fmt.Fprintln(os.Stderr, args...) } } -func jsonTrace(args ...interface{}) { +func traceJSON(args ...interface{}) { if ReaderTrace { for _, arg := range args { jsonBytes, jsonErr := json.MarshalIndent(arg, "", " ") @@ -231,7 +231,7 @@ func readField(astField *ast.Field, fileImports fileImportSpecMap) (name string, name = astField.Names[0].Name } - trace(" reading field with name", name, "of type", astField.Type) + // trace(" reading field with name", name, "of type", astField.Type) v = &Value{} v.loadExpr(astField.Type, fileImports) if astField.Tag != nil { @@ -264,11 +264,7 @@ func readFieldList(fieldList []*ast.Field, fileImports fileImportSpecMap) (field } func extractStructs(file *ast.File, packageName string, structs map[string]*Struct) error { - trace("reading file", file.Name.Name) - //for _, imp := range file.Imports { - // fmt.Println("import", imp.Name, imp.Path) - //} fileImports := getFileImports(file, packageName) for name, obj := range file.Scope.Objects { //fmt.Println(name, obj.Kind, obj.Data) diff --git a/typescript.go b/typescript.go index 157ab75..d1ba601 100644 --- a/typescript.go +++ b/typescript.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "strings" + + "github.com/foomo/gotsrpc/config" ) func (f *Field) tsName() string { @@ -14,18 +16,23 @@ func (f *Field) tsName() string { return n } -func (v *Value) tsType() string { +func (v *Value) tsType(mappings config.TypeScriptMappings) string { switch true { case v.IsPtr: if v.StructType != nil { if len(v.StructType.Package) > 0 { - return v.StructType.Package + "." + v.StructType.Name + mapping, ok := mappings[v.StructType.Package] + var tsModule string + if ok { + tsModule = mapping.TypeScriptModule + } + return tsModule + "." + v.StructType.Name } return v.StructType.Name } return string(v.ScalarType) case v.Array != nil: - return v.Array.Value.tsType() + "[]" + return v.Array.Value.tsType(mappings) + "[]" case len(v.ScalarType) > 0: switch v.ScalarType { case ScalarTypeBool: @@ -39,8 +46,8 @@ func (v *Value) tsType() string { } } -func renderStruct(str *Struct, ts *code) error { - ts.l("// " + str.Package + "." + str.Name).ind(1) +func renderStruct(str *Struct, mappings config.TypeScriptMappings, ts *code) error { + ts.l("// " + str.FullName()) ts.l("export interface " + str.Name + " {").ind(1) for _, f := range str.Fields { if f.JSONInfo != nil && f.JSONInfo.Ignore { @@ -50,7 +57,7 @@ func renderStruct(str *Struct, ts *code) error { if f.Value.IsPtr { ts.app("?") } - ts.app(":" + f.Value.tsType()) + ts.app(":" + f.Value.tsType(mappings)) ts.app(";") ts.nl() } @@ -68,7 +75,7 @@ func renderStruct(str *Struct, ts *code) error { } */ -func renderService(service *Service, ts *code) error { +func renderService(service *Service, mappings config.TypeScriptMappings, ts *code) error { clientName := service.Name + "Client" ts.l("export class " + clientName + " {").ind(1). l("static defaultInst = new " + clientName + ";"). @@ -90,7 +97,7 @@ func renderService(service *Service, ts *code) error { continue } - args = append(args, arg.tsName()+":"+arg.Value.tsType()) + args = append(args, arg.tsName()+":"+arg.Value.tsType(mappings)) callArgs = append(callArgs, arg.Name) } ts.app(strings.Join(args, ", ")) @@ -104,7 +111,7 @@ func renderService(service *Service, ts *code) error { retArgName += "_" + fmt.Sprint(index) } } - retArgs = append(retArgs, retArgName+":"+retField.Value.tsType()) + retArgs = append(retArgs, retArgName+":"+retField.Value.tsType(mappings)) } if len(args) > 0 { ts.app(", ") @@ -122,8 +129,33 @@ func renderService(service *Service, ts *code) error { ts.l("}") return nil } - -func RenderTypeScript(services []*Service, structs map[string]*Struct, tsModuleName string) (typeScript string, err error) { +func RenderStructsToPackages(structs map[string]*Struct, mappings config.TypeScriptMappings) (mappedTypeScript map[string]string, err error) { + mappedTypeScript = map[string]string{} + codeMap := map[string]*code{} + for _, mapping := range mappings { + codeMap[mapping.GoPackage] = newCode().l("module " + mapping.TypeScriptModule + " {").ind(1) + } + for name, str := range structs { + if str == nil { + err = errors.New("could not resolve: " + name) + return + } + ts, ok := codeMap[str.Package] + if !ok { + err = errors.New("missing code mapping for: " + str.Package) + return + } + err = renderStruct(str, mappings, ts) + if err != nil { + return + } + } + for _, mapping := range mappings { + mappedTypeScript[mapping.TypeScriptModule] = codeMap[mapping.GoPackage].ind(-1).l("}").string() + } + return +} +func RenderTypeScriptServices(services []*Service, mappings config.TypeScriptMappings, tsModuleName string) (typeScript string, err error) { ts := newCode() ts.l(`module GoTSRPC { export function call(endPoint:string, method:string, args:any[], success:any, err:any) { @@ -153,18 +185,8 @@ func RenderTypeScript(services []*Service, structs map[string]*Struct, tsModuleN ts.l("module " + tsModuleName + " {") ts.ind(1) - for name, str := range structs { - if str == nil { - return "", errors.New("could not resolve: " + name) - } - err = renderStruct(str, ts) - if err != nil { - return - } - } - for _, service := range services { - err = renderService(service, ts) + err = renderService(service, mappings, ts) if err != nil { return }