mirror of
https://github.com/foomo/gotsrpc.git
synced 2025-10-16 12:35:35 +00:00
async client flavor added
This commit is contained in:
parent
dcc105e15f
commit
a8855fa401
40
README.md
40
README.md
@ -38,6 +38,8 @@ Will generate client and server side go and TypeScript code. Have fun!
|
|||||||
```yaml
|
```yaml
|
||||||
---
|
---
|
||||||
modulekind: commonjs
|
modulekind: commonjs
|
||||||
|
# if you want an async api vs classic callbacks - here you are
|
||||||
|
tsclientflavor: async
|
||||||
targets:
|
targets:
|
||||||
demo:
|
demo:
|
||||||
services:
|
services:
|
||||||
@ -59,6 +61,44 @@ mappings:
|
|||||||
out: /tmp/test-files-demo-nested.ts
|
out: /tmp/test-files-demo-nested.ts
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
#### an async example
|
||||||
|
|
||||||
|
How to use async clients in this case with axios:
|
||||||
|
|
||||||
|
```TypeScript
|
||||||
|
import axios, { AxiosPromise } from "axios";
|
||||||
|
import { ServiceClient as ExampleClient } from "./some/generated/client";
|
||||||
|
|
||||||
|
// axios transport
|
||||||
|
let getTransport = endpoint => async <T>(method, args = []) => {
|
||||||
|
return new Promise<T>(async (resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let axiosPromise: any = await axios.post<T>(
|
||||||
|
endpoint + encodeURIComponent(method),
|
||||||
|
JSON.stringify(args),
|
||||||
|
);
|
||||||
|
return resolve(axiosPromise.data);
|
||||||
|
} catch (e) {
|
||||||
|
return reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let client = new ExampleClient(getTransport(ExampleClient.defaultEndpoint));
|
||||||
|
|
||||||
|
export async function test() {
|
||||||
|
try {
|
||||||
|
let result = await client.getResult();
|
||||||
|
console.log("here is the result", result);
|
||||||
|
} catch (e) {
|
||||||
|
// e => network?
|
||||||
|
// e => json
|
||||||
|
// e => domain error type
|
||||||
|
console.error("something went wrong ...", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### oldschool TypeScript
|
### oldschool TypeScript
|
||||||
|
|
||||||
|
|||||||
5
build.go
5
build.go
@ -110,7 +110,7 @@ func Build(conf *config.Config, goPath string) {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
ts, err := RenderTypeScriptServices(conf.ModuleKind, services, conf.Mappings, scalarTypes, target)
|
ts, err := RenderTypeScriptServices(conf.ModuleKind, conf.TSClientFlavor, services, conf.Mappings, scalarTypes, target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, " could not generate ts code", err)
|
fmt.Fprintln(os.Stderr, " could not generate ts code", err)
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
@ -128,7 +128,8 @@ func Build(conf *config.Config, goPath string) {
|
|||||||
fmt.Fprintln(os.Stderr, " could not write service file", target.Out, updateErr)
|
fmt.Fprintln(os.Stderr, " could not write service file", target.Out, updateErr)
|
||||||
os.Exit(3)
|
os.Exit(3)
|
||||||
}
|
}
|
||||||
err = RenderStructsToPackages(structs, conf.Mappings, constants, scalarTypes, mappedTypeScript)
|
|
||||||
|
err = renderTypescriptStructsToPackages(conf.ModuleKind, structs, conf.Mappings, constants, scalarTypes, mappedTypeScript)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, "struct gen err for target", name, err)
|
fmt.Fprintln(os.Stderr, "struct gen err for target", name, err)
|
||||||
os.Exit(4)
|
os.Exit(4)
|
||||||
|
|||||||
@ -27,11 +27,10 @@ var GoTSRPC;
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
})(GoTSRPC || (GoTSRPC = {})); // close
|
})(GoTSRPC || (GoTSRPC = {})); // close
|
||||||
var GoTSRPC;
|
|
||||||
(function (GoTSRPC) {
|
(function (GoTSRPC) {
|
||||||
var Demo;
|
var Demo;
|
||||||
(function (Demo) {
|
(function (Demo) {
|
||||||
var FooClient = (function () {
|
var FooClient = /** @class */ (function () {
|
||||||
function FooClient(endPoint, transport) {
|
function FooClient(endPoint, transport) {
|
||||||
if (endPoint === void 0) { endPoint = "/service/foo"; }
|
if (endPoint === void 0) { endPoint = "/service/foo"; }
|
||||||
if (transport === void 0) { transport = GoTSRPC.call; }
|
if (transport === void 0) { transport = GoTSRPC.call; }
|
||||||
@ -45,7 +44,7 @@ var GoTSRPC;
|
|||||||
return FooClient;
|
return FooClient;
|
||||||
}());
|
}());
|
||||||
Demo.FooClient = FooClient;
|
Demo.FooClient = FooClient;
|
||||||
var DemoClient = (function () {
|
var DemoClient = /** @class */ (function () {
|
||||||
function DemoClient(endPoint, transport) {
|
function DemoClient(endPoint, transport) {
|
||||||
if (endPoint === void 0) { endPoint = "/service/demo"; }
|
if (endPoint === void 0) { endPoint = "/service/demo"; }
|
||||||
if (transport === void 0) { transport = GoTSRPC.call; }
|
if (transport === void 0) { transport = GoTSRPC.call; }
|
||||||
@ -64,6 +63,9 @@ var GoTSRPC;
|
|||||||
DemoClient.prototype.helloInterface = function (anything, anythingMap, anythingSlice, success, err) {
|
DemoClient.prototype.helloInterface = function (anything, anythingMap, anythingSlice, success, err) {
|
||||||
this.transport(this.endPoint, "HelloInterface", [anything, anythingMap, anythingSlice], success, err);
|
this.transport(this.endPoint, "HelloInterface", [anything, anythingMap, anythingSlice], success, err);
|
||||||
};
|
};
|
||||||
|
DemoClient.prototype.helloScalarError = function (success, err) {
|
||||||
|
this.transport(this.endPoint, "HelloScalarError", [], success, err);
|
||||||
|
};
|
||||||
DemoClient.prototype.mapCrap = function (success, err) {
|
DemoClient.prototype.mapCrap = function (success, err) {
|
||||||
this.transport(this.endPoint, "MapCrap", [], success, err);
|
this.transport(this.endPoint, "MapCrap", [], success, err);
|
||||||
};
|
};
|
||||||
|
|||||||
4
code.go
4
code.go
@ -42,5 +42,9 @@ func (c *code) app(str string) *code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *code) string() string {
|
func (c *code) string() string {
|
||||||
|
if c.line != "" {
|
||||||
|
c.lines = append(c.lines, c.line)
|
||||||
|
c.line = ""
|
||||||
|
}
|
||||||
return strings.Join(c.lines, "\n")
|
return strings.Join(c.lines, "\n")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -65,16 +65,19 @@ type Mapping struct {
|
|||||||
type TypeScriptMappings map[string]*Mapping
|
type TypeScriptMappings map[string]*Mapping
|
||||||
|
|
||||||
type ModuleKind string
|
type ModuleKind string
|
||||||
|
type TSClientFlavor string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ModuleKindDefault ModuleKind = "default"
|
ModuleKindDefault ModuleKind = "default"
|
||||||
ModuleKindCommonJS ModuleKind = "commonjs"
|
ModuleKindCommonJS ModuleKind = "commonjs"
|
||||||
|
TSClientFlavorAsync TSClientFlavor = "async"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
ModuleKind ModuleKind
|
ModuleKind ModuleKind
|
||||||
Targets map[string]*Target
|
TSClientFlavor TSClientFlavor
|
||||||
Mappings TypeScriptMappings
|
Targets map[string]*Target
|
||||||
|
Mappings TypeScriptMappings
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadConfigFile(file string) (conf *Config, err error) {
|
func LoadConfigFile(file string) (conf *Config, err error) {
|
||||||
@ -93,6 +96,11 @@ func loadConfig(yamlBytes []byte) (conf *Config, err error) {
|
|||||||
err = errors.New("could not parse yaml: " + yamlErr.Error())
|
err = errors.New("could not parse yaml: " + yamlErr.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
switch conf.TSClientFlavor {
|
||||||
|
case "", TSClientFlavorAsync:
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown ts client flavor: " + conf.TSClientFlavor + " must be empty or " + TSClientFlavorAsync)
|
||||||
|
}
|
||||||
switch conf.ModuleKind {
|
switch conf.ModuleKind {
|
||||||
case ModuleKindCommonJS, ModuleKindDefault:
|
case ModuleKindCommonJS, ModuleKindDefault:
|
||||||
case "":
|
case "":
|
||||||
|
|||||||
2
model.go
2
model.go
@ -23,6 +23,7 @@ type StructType struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Value struct {
|
type Value struct {
|
||||||
|
IsError bool `json:",omitempty"`
|
||||||
IsInterface bool `json:",omitempty"`
|
IsInterface bool `json:",omitempty"`
|
||||||
Scalar *Scalar `json:",omitempty"`
|
Scalar *Scalar `json:",omitempty"`
|
||||||
ScalarType ScalarType `json:",omitempty"`
|
ScalarType ScalarType `json:",omitempty"`
|
||||||
@ -88,6 +89,7 @@ type Method struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Struct struct {
|
type Struct struct {
|
||||||
|
IsError bool
|
||||||
Package string
|
Package string
|
||||||
Name string
|
Name string
|
||||||
Fields []*Field
|
Fields []*Field
|
||||||
|
|||||||
@ -259,11 +259,13 @@ func Read(goPaths []string, packageName string, serviceMap map[string]string) (s
|
|||||||
|
|
||||||
func fixFieldStructs(fields []*Field, structs map[string]*Struct, scalars map[string]*Scalar) {
|
func fixFieldStructs(fields []*Field, structs map[string]*Struct, scalars map[string]*Scalar) {
|
||||||
for _, f := range fields {
|
for _, f := range fields {
|
||||||
|
|
||||||
if f.Value.StructType != nil {
|
if f.Value.StructType != nil {
|
||||||
// do we have that struct or is it a hidden scalar
|
// do we have that struct or is it a hidden scalar
|
||||||
name := f.Value.StructType.FullName()
|
name := f.Value.StructType.FullName()
|
||||||
_, strctExists := structs[name]
|
s, strctExists := structs[name]
|
||||||
if strctExists {
|
if strctExists {
|
||||||
|
f.Value.IsError = s.IsError
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
scalar, scalarExists := scalars[name]
|
scalar, scalarExists := scalars[name]
|
||||||
|
|||||||
@ -15,13 +15,27 @@ func readStructs(pkg *ast.Package, packageName string) (structs map[string]*Stru
|
|||||||
structs = map[string]*Struct{}
|
structs = map[string]*Struct{}
|
||||||
trace("reading files in package", packageName)
|
trace("reading files in package", packageName)
|
||||||
scalarTypes = map[string]*Scalar{}
|
scalarTypes = map[string]*Scalar{}
|
||||||
|
errorTypes := map[string]bool{}
|
||||||
for _, file := range pkg.Files {
|
for _, file := range pkg.Files {
|
||||||
err = extractTypes(file, packageName, structs, scalarTypes)
|
err = extractTypes(file, packageName, structs, scalarTypes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = extractErrorTypes(file, packageName, errorTypes)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// jsonDump(scalarTypes)
|
for name, structType := range structs {
|
||||||
|
_, isErrorType := errorTypes[name]
|
||||||
|
if isErrorType {
|
||||||
|
structType.IsError = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//jsonDump(errorTypes)
|
||||||
|
//jsonDump(scalarTypes)
|
||||||
|
//jsonDump(structs)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,8 +305,36 @@ func readFieldList(fieldList []*ast.Field, fileImports fileImportSpecMap) (field
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractErrorTypes(file *ast.File, packageName string, errorTypes map[string]bool) (err error) {
|
||||||
|
for _, d := range file.Decls {
|
||||||
|
if reflect.ValueOf(d).Type().String() == "*ast.FuncDecl" {
|
||||||
|
funcDecl := d.(*ast.FuncDecl)
|
||||||
|
if funcDecl.Recv != nil && len(funcDecl.Recv.List) == 1 {
|
||||||
|
firstReceiverField := funcDecl.Recv.List[0]
|
||||||
|
if "*ast.StarExpr" == reflect.ValueOf(firstReceiverField.Type).Type().String() {
|
||||||
|
starExpr := firstReceiverField.Type.(*ast.StarExpr)
|
||||||
|
if "*ast.Ident" == reflect.ValueOf(starExpr.X).Type().String() {
|
||||||
|
ident := starExpr.X.(*ast.Ident)
|
||||||
|
if funcDecl.Name.Name == "Error" && funcDecl.Type.Params.NumFields() == 0 && funcDecl.Type.Results.NumFields() == 1 {
|
||||||
|
returnValueField := funcDecl.Type.Results.List[0]
|
||||||
|
refl := reflect.ValueOf(returnValueField.Type)
|
||||||
|
if refl.Type().String() == "*ast.Ident" {
|
||||||
|
returnValueIdent := returnValueField.Type.(*ast.Ident)
|
||||||
|
if returnValueIdent.Name == "string" {
|
||||||
|
errorTypes[packageName+"."+ident.Name] = true
|
||||||
|
}
|
||||||
|
//fmt.Println("error for:", ident.Name, returnValueIdent.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func extractTypes(file *ast.File, packageName string, structs map[string]*Struct, scalarTypes map[string]*Scalar) error {
|
func extractTypes(file *ast.File, packageName string, structs map[string]*Struct, scalarTypes map[string]*Scalar) error {
|
||||||
trace("reading file", file.Name.Name)
|
|
||||||
fileImports := getFileImports(file, packageName)
|
fileImports := getFileImports(file, packageName)
|
||||||
for name, obj := range file.Scope.Objects {
|
for name, obj := range file.Scope.Objects {
|
||||||
if obj.Kind == ast.Typ && obj.Decl != nil {
|
if obj.Kind == ast.Typ && obj.Decl != nil {
|
||||||
|
|||||||
121
typescript.go
121
typescript.go
@ -95,7 +95,7 @@ func renderStructFields(fields []*Field, mappings config.TypeScriptMappings, sca
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderStruct(str *Struct, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, ts *code) error {
|
func renderTypescriptStruct(str *Struct, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, ts *code) error {
|
||||||
ts.l("// " + str.FullName())
|
ts.l("// " + str.FullName())
|
||||||
ts.l("export interface " + str.Name + " {").ind(1)
|
ts.l("export interface " + str.Name + " {").ind(1)
|
||||||
renderStructFields(str.Fields, mappings, scalarTypes, ts)
|
renderStructFields(str.Fields, mappings, scalarTypes, ts)
|
||||||
@ -103,92 +103,14 @@ func renderStruct(str *Struct, mappings config.TypeScriptMappings, scalarTypes m
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderService(skipGoTSRPC bool, moduleKind config.ModuleKind, service *Service, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, ts *code) error {
|
func renderTypescriptStructsToPackages(
|
||||||
clientName := service.Name + "Client"
|
moduleKind config.ModuleKind,
|
||||||
|
structs map[string]*Struct,
|
||||||
ts.l("export class " + clientName + " {").ind(1)
|
mappings config.TypeScriptMappings,
|
||||||
|
constants map[string]map[string]*ast.BasicLit,
|
||||||
if moduleKind == config.ModuleKindCommonJS {
|
scalarTypes map[string]*Scalar,
|
||||||
if skipGoTSRPC {
|
mappedTypeScript map[string]map[string]*code,
|
||||||
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport:(endPoint:string, method:string, args:any[], success:any, err:any) => void) { }")
|
) (err error) {
|
||||||
} else {
|
|
||||||
ts.l("static defaultInst = new " + clientName + ";")
|
|
||||||
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport = call) { }")
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
ts.l("static defaultInst = new " + clientName + ";")
|
|
||||||
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport = GoTSRPC.call) { }")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, method := range service.Methods {
|
|
||||||
|
|
||||||
ts.app(lcfirst(method.Name) + "(")
|
|
||||||
// actual args
|
|
||||||
//args := []string{}
|
|
||||||
callArgs := []string{}
|
|
||||||
|
|
||||||
argOffset := 0
|
|
||||||
for index, arg := range method.Args {
|
|
||||||
if index == 0 && arg.Value.isHTTPResponseWriter() {
|
|
||||||
trace("skipping first arg is a http.ResponseWriter")
|
|
||||||
argOffset = 1
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if index == 1 && arg.Value.isHTTPRequest() {
|
|
||||||
trace("skipping second arg is a *http.Request")
|
|
||||||
argOffset = 2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
argCount := 0
|
|
||||||
for index, arg := range method.Args {
|
|
||||||
if index < argOffset {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if index > argOffset {
|
|
||||||
ts.app(", ")
|
|
||||||
}
|
|
||||||
ts.app(arg.tsName() + ":")
|
|
||||||
arg.Value.tsType(mappings, scalarTypes, ts)
|
|
||||||
callArgs = append(callArgs, arg.Name)
|
|
||||||
argCount++
|
|
||||||
}
|
|
||||||
if argCount > 0 {
|
|
||||||
ts.app(", ")
|
|
||||||
}
|
|
||||||
ts.app("success:(")
|
|
||||||
// + strings.Join(retArgs, ", ") +
|
|
||||||
|
|
||||||
for index, retField := range method.Return {
|
|
||||||
retArgName := retField.tsName()
|
|
||||||
if len(retArgName) == 0 {
|
|
||||||
retArgName = "ret"
|
|
||||||
if index > 0 {
|
|
||||||
retArgName += "_" + fmt.Sprint(index)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if index > 0 {
|
|
||||||
ts.app(", ")
|
|
||||||
}
|
|
||||||
ts.app(retArgName + ":")
|
|
||||||
retField.Value.tsType(mappings, scalarTypes, ts)
|
|
||||||
}
|
|
||||||
|
|
||||||
ts.app(") => void")
|
|
||||||
ts.app(", err:(request:XMLHttpRequest, e?:Error) => void) {").nl()
|
|
||||||
ts.ind(1)
|
|
||||||
// generic framework call
|
|
||||||
ts.l("this.transport(this.endPoint, \"" + method.Name + "\", [" + strings.Join(callArgs, ", ") + "], success, err);")
|
|
||||||
ts.ind(-1)
|
|
||||||
ts.app("}")
|
|
||||||
ts.nl()
|
|
||||||
}
|
|
||||||
ts.ind(-1)
|
|
||||||
ts.l("}")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func RenderStructsToPackages(structs map[string]*Struct, mappings config.TypeScriptMappings, constants map[string]map[string]*ast.BasicLit, scalarTypes map[string]*Scalar, mappedTypeScript map[string]map[string]*code) (err error) {
|
|
||||||
|
|
||||||
codeMap := map[string]map[string]*code{}
|
codeMap := map[string]map[string]*code{}
|
||||||
for _, mapping := range mappings {
|
for _, mapping := range mappings {
|
||||||
@ -204,8 +126,12 @@ func RenderStructsToPackages(structs map[string]*Struct, mappings config.TypeScr
|
|||||||
err = errors.New("missing code mapping for go package : " + str.Package + " => you have to add a mapping from this go package to a TypeScript module in your build-config.yml in the mappings section")
|
err = errors.New("missing code mapping for go package : " + str.Package + " => you have to add a mapping from this go package to a TypeScript module in your build-config.yml in the mappings section")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
packageCodeMap[str.Name] = newCode(" ").ind(1)
|
packageCodeMap[str.Name] = newCode(" ")
|
||||||
err = renderStruct(str, mappings, scalarTypes, packageCodeMap[str.Name])
|
// fmt.Println("--------------------------->", moduleKind == config.ModuleKindCommonJS)
|
||||||
|
if !(moduleKind == config.ModuleKindCommonJS) {
|
||||||
|
packageCodeMap[str.Name].ind(1)
|
||||||
|
}
|
||||||
|
err = renderTypescriptStruct(str, mappings, scalarTypes, packageCodeMap[str.Name])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -230,7 +156,11 @@ func RenderStructsToPackages(structs map[string]*Struct, mappings config.TypeScr
|
|||||||
if done {
|
if done {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
constCode := newCode(" ").ind(1).l("// constants from " + packageName).l("export const GoConst = {").ind(1)
|
constCode := newCode(" ")
|
||||||
|
if moduleKind != config.ModuleKindCommonJS {
|
||||||
|
constCode.ind(1)
|
||||||
|
}
|
||||||
|
constCode.l("// constants from " + packageName).l("export const GoConst = {").ind(1)
|
||||||
//constCode.l()
|
//constCode.l()
|
||||||
mappedTypeScript[packageName][goConstPseudoPackage] = constCode
|
mappedTypeScript[packageName][goConstPseudoPackage] = constCode
|
||||||
constPrefixParts := split(packageName, []string{"/", ".", "-"})
|
constPrefixParts := split(packageName, []string{"/", ".", "-"})
|
||||||
@ -289,9 +219,9 @@ func ucFirst(str string) string {
|
|||||||
return constPrefix
|
return constPrefix
|
||||||
}
|
}
|
||||||
|
|
||||||
func RenderTypeScriptServices(moduleKind config.ModuleKind, services ServiceList, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, target *config.Target) (typeScript string, err error) {
|
func RenderTypeScriptServices(moduleKind config.ModuleKind, tsClientFlavor config.TSClientFlavor, services ServiceList, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, target *config.Target) (typeScript string, err error) {
|
||||||
ts := newCode(" ")
|
ts := newCode(" ")
|
||||||
if !SkipGoTSRPC {
|
if !SkipGoTSRPC && tsClientFlavor == "" {
|
||||||
|
|
||||||
if moduleKind != config.ModuleKindCommonJS {
|
if moduleKind != config.ModuleKindCommonJS {
|
||||||
ts.l(`module GoTSRPC {`)
|
ts.l(`module GoTSRPC {`)
|
||||||
@ -337,7 +267,12 @@ func RenderTypeScriptServices(moduleKind config.ModuleKind, services ServiceList
|
|||||||
if !target.IsTSRPC(service.Name) {
|
if !target.IsTSRPC(service.Name) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = renderService(SkipGoTSRPC, moduleKind, service, mappings, scalarTypes, ts)
|
switch tsClientFlavor {
|
||||||
|
case config.TSClientFlavorAsync:
|
||||||
|
err = renderTypescriptClientAsync(service, mappings, scalarTypes, ts)
|
||||||
|
default:
|
||||||
|
err = renderTypescriptClient(SkipGoTSRPC, moduleKind, service, mappings, scalarTypes, ts)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,5 +10,4 @@ func TestSplit(t *testing.T) {
|
|||||||
t.Fatal("expected", expected, "got", actual)
|
t.Fatal("expected", expected, "got", actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
94
typescriptclient.go
Normal file
94
typescriptclient.go
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
package gotsrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/foomo/gotsrpc/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func renderTypescriptClient(skipGoTSRPC bool, moduleKind config.ModuleKind, service *Service, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, ts *code) error {
|
||||||
|
clientName := service.Name + "Client"
|
||||||
|
|
||||||
|
ts.l("export class " + clientName + " {").ind(1)
|
||||||
|
|
||||||
|
if moduleKind == config.ModuleKindCommonJS {
|
||||||
|
if skipGoTSRPC {
|
||||||
|
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport:(endPoint:string, method:string, args:any[], success:any, err:any) => void) { }")
|
||||||
|
} else {
|
||||||
|
ts.l("static defaultInst = new " + clientName + ";")
|
||||||
|
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport = call) { }")
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ts.l("static defaultInst = new " + clientName + ";")
|
||||||
|
ts.l("constructor(public endPoint:string = \"" + service.Endpoint + "\", public transport = GoTSRPC.call) { }")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
|
||||||
|
ts.app(lcfirst(method.Name) + "(")
|
||||||
|
// actual args
|
||||||
|
//args := []string{}
|
||||||
|
callArgs := []string{}
|
||||||
|
|
||||||
|
argOffset := 0
|
||||||
|
for index, arg := range method.Args {
|
||||||
|
if index == 0 && arg.Value.isHTTPResponseWriter() {
|
||||||
|
trace("skipping first arg is a http.ResponseWriter")
|
||||||
|
argOffset = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if index == 1 && arg.Value.isHTTPRequest() {
|
||||||
|
trace("skipping second arg is a *http.Request")
|
||||||
|
argOffset = 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argCount := 0
|
||||||
|
for index, arg := range method.Args {
|
||||||
|
if index < argOffset {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if index > argOffset {
|
||||||
|
ts.app(", ")
|
||||||
|
}
|
||||||
|
ts.app(arg.tsName() + ":")
|
||||||
|
arg.Value.tsType(mappings, scalarTypes, ts)
|
||||||
|
callArgs = append(callArgs, arg.Name)
|
||||||
|
argCount++
|
||||||
|
}
|
||||||
|
if argCount > 0 {
|
||||||
|
ts.app(", ")
|
||||||
|
}
|
||||||
|
ts.app("success:(")
|
||||||
|
// + strings.Join(retArgs, ", ") +
|
||||||
|
|
||||||
|
for index, retField := range method.Return {
|
||||||
|
retArgName := retField.tsName()
|
||||||
|
if len(retArgName) == 0 {
|
||||||
|
retArgName = "ret"
|
||||||
|
if index > 0 {
|
||||||
|
retArgName += "_" + fmt.Sprint(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index > 0 {
|
||||||
|
ts.app(", ")
|
||||||
|
}
|
||||||
|
ts.app(retArgName + ":")
|
||||||
|
retField.Value.tsType(mappings, scalarTypes, ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.app(") => void")
|
||||||
|
ts.app(", err:(request:XMLHttpRequest, e?:Error) => void) {").nl()
|
||||||
|
ts.ind(1)
|
||||||
|
// generic framework call
|
||||||
|
ts.l("this.transport(this.endPoint, \"" + method.Name + "\", [" + strings.Join(callArgs, ", ") + "], success, err);")
|
||||||
|
ts.ind(-1)
|
||||||
|
ts.app("}")
|
||||||
|
ts.nl()
|
||||||
|
}
|
||||||
|
ts.ind(-1)
|
||||||
|
ts.l("}")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
159
typscriptclientasync.go
Normal file
159
typscriptclientasync.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package gotsrpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/foomo/gotsrpc/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func renderTypescriptClientAsync(service *Service, mappings config.TypeScriptMappings, scalarTypes map[string]*Scalar, ts *code) error {
|
||||||
|
clientName := service.Name + "Client"
|
||||||
|
|
||||||
|
ts.l("export class " + clientName + " {")
|
||||||
|
|
||||||
|
ts.ind(1)
|
||||||
|
//ts.l(`static defaultInst = new ` + clientName + `()`)
|
||||||
|
//ts.l(`constructor(public endpoint = "` + service.Endpoint + `") {}`)
|
||||||
|
ts.l(`public static defaultEndpoint = "` + service.Endpoint + `";`)
|
||||||
|
ts.l("constructor(")
|
||||||
|
ts.ind(1)
|
||||||
|
ts.l("public transport:<T>(method: string, data?: any[]) => Promise<T>")
|
||||||
|
ts.ind(-1)
|
||||||
|
ts.l(") {}")
|
||||||
|
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
ts.app("async " + lcfirst(method.Name) + "(")
|
||||||
|
callArgs := []string{}
|
||||||
|
argOffset := 0
|
||||||
|
for index, arg := range method.Args {
|
||||||
|
if index == 0 && arg.Value.isHTTPResponseWriter() {
|
||||||
|
trace("skipping first arg is a http.ResponseWriter")
|
||||||
|
argOffset = 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if index == 1 && arg.Value.isHTTPRequest() {
|
||||||
|
trace("skipping second arg is a *http.Request")
|
||||||
|
argOffset = 2
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argCount := 0
|
||||||
|
for index, arg := range method.Args {
|
||||||
|
if index < argOffset {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if index > argOffset {
|
||||||
|
ts.app(", ")
|
||||||
|
}
|
||||||
|
ts.app(arg.tsName() + ":")
|
||||||
|
arg.Value.tsType(mappings, scalarTypes, ts)
|
||||||
|
callArgs = append(callArgs, arg.Name)
|
||||||
|
argCount++
|
||||||
|
}
|
||||||
|
ts.app("):")
|
||||||
|
|
||||||
|
throwLastError := false
|
||||||
|
//lastErrorName := ""
|
||||||
|
|
||||||
|
returnTypeTS := newCode(" ")
|
||||||
|
returnTypeTS.app("{")
|
||||||
|
innerReturnTypeTS := newCode(" ")
|
||||||
|
innerReturnTypeTS.app("{")
|
||||||
|
firstReturnType := ""
|
||||||
|
//firstReturnFieldName := ""
|
||||||
|
countReturns := 0
|
||||||
|
countInnerReturns := 0
|
||||||
|
errIndex := 0
|
||||||
|
responseObjectPrefix := ""
|
||||||
|
responseObject := "let responseObject = {"
|
||||||
|
|
||||||
|
for index, retField := range method.Return {
|
||||||
|
countInnerReturns++
|
||||||
|
retArgName := retField.tsName()
|
||||||
|
|
||||||
|
if len(retArgName) == 0 {
|
||||||
|
retArgName = "ret"
|
||||||
|
if index > 0 {
|
||||||
|
retArgName += "_" + fmt.Sprint(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if index > 0 {
|
||||||
|
returnTypeTS.app("; ")
|
||||||
|
innerReturnTypeTS.app("; ")
|
||||||
|
}
|
||||||
|
|
||||||
|
innerReturnTypeTS.app(strconv.Itoa(index) + ":")
|
||||||
|
retField.Value.tsType(mappings, scalarTypes, innerReturnTypeTS)
|
||||||
|
|
||||||
|
if index == len(method.Return)-1 && retField.Value.IsError {
|
||||||
|
throwLastError = true
|
||||||
|
//lastErrorName = retArgName
|
||||||
|
errIndex = index
|
||||||
|
} else {
|
||||||
|
if index == 0 {
|
||||||
|
firstReturnTypeTS := newCode(" ")
|
||||||
|
retField.Value.tsType(mappings, scalarTypes, firstReturnTypeTS)
|
||||||
|
firstReturnType = firstReturnTypeTS.string()
|
||||||
|
//firstReturnFieldName = retArgName
|
||||||
|
}
|
||||||
|
countReturns++
|
||||||
|
returnTypeTS.app(retArgName + ":")
|
||||||
|
responseObject += responseObjectPrefix + retArgName + " : response[" + strconv.Itoa(index) + "]"
|
||||||
|
retField.Value.tsType(mappings, scalarTypes, returnTypeTS)
|
||||||
|
}
|
||||||
|
responseObjectPrefix = ", "
|
||||||
|
}
|
||||||
|
responseObject += "};"
|
||||||
|
returnTypeTS.app("}")
|
||||||
|
innerReturnTypeTS.app("}")
|
||||||
|
if countReturns == 0 {
|
||||||
|
ts.app("Promise<void> {")
|
||||||
|
} else if countReturns == 1 {
|
||||||
|
ts.app("Promise<" + firstReturnType + "> {")
|
||||||
|
} else if countReturns > 1 {
|
||||||
|
ts.app("Promise<" + returnTypeTS.string() + "> {")
|
||||||
|
}
|
||||||
|
ts.nl()
|
||||||
|
|
||||||
|
ts.ind(1)
|
||||||
|
|
||||||
|
innerCallTypeString := "void"
|
||||||
|
if countInnerReturns > 0 {
|
||||||
|
innerCallTypeString = innerReturnTypeTS.string()
|
||||||
|
}
|
||||||
|
|
||||||
|
call := "this.transport<" + innerCallTypeString + ">(\"" + method.Name + "\", [" + strings.Join(callArgs, ", ") + "])"
|
||||||
|
if throwLastError {
|
||||||
|
ts.l("let response = await " + call)
|
||||||
|
|
||||||
|
ts.l("let err = response[" + strconv.Itoa(errIndex) + "];")
|
||||||
|
//ts.l("delete response." + lastErrorName + ";")
|
||||||
|
ts.l("if(err) { throw err }")
|
||||||
|
if countReturns == 1 {
|
||||||
|
ts.l("return response[0]")
|
||||||
|
} else if countReturns == 0 {
|
||||||
|
//ts.l("return response;")
|
||||||
|
} else {
|
||||||
|
ts.l(responseObject)
|
||||||
|
ts.l("return responseObject;")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if countReturns == 1 {
|
||||||
|
ts.l("return (await " + call + ")[0]")
|
||||||
|
} else {
|
||||||
|
ts.l("let response = await " + call)
|
||||||
|
ts.l(responseObject)
|
||||||
|
ts.l("return responseObject;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ts.ind(-1)
|
||||||
|
ts.app("}")
|
||||||
|
ts.nl()
|
||||||
|
}
|
||||||
|
ts.ind(-1)
|
||||||
|
ts.l("}")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user