Refactor to improve later; fix comments in single line
parent
71e7c2f72f
commit
77cadb7a0d
|
@ -0,0 +1,3 @@
|
||||||
|
gofname: test/goapi/api.go
|
||||||
|
goimpldir: test/goapi
|
||||||
|
tsfname: test/tscli/api.ts
|
|
@ -0,0 +1,68 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func manageComments(a *ast.CommentGroup) map[string]string {
|
||||||
|
ret := make(map[string]string)
|
||||||
|
|
||||||
|
if a == nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
var myExp = regexp.MustCompile(`\/?\*?@(?P<k>.*)\s*:\s*(?P<v>.*)\*?\/?`)
|
||||||
|
|
||||||
|
var myExp2 = regexp.MustCompile(`\/?\*?@(?P<k>.*?)\*?\/?$`)
|
||||||
|
|
||||||
|
for _, v := range a.List {
|
||||||
|
lines := strings.Split(v.Text, "\n")
|
||||||
|
for _, l := range lines {
|
||||||
|
m := myExp.FindStringSubmatch(l)
|
||||||
|
if m != nil {
|
||||||
|
ret[m[1]] = m[2]
|
||||||
|
} else {
|
||||||
|
m := myExp2.FindStringSubmatch(l)
|
||||||
|
if m != nil {
|
||||||
|
ret[m[1]] = "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func manageCommentsGroups(as []*ast.CommentGroup) map[string]string {
|
||||||
|
ret := make(map[string]string)
|
||||||
|
|
||||||
|
if as == nil {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range as {
|
||||||
|
|
||||||
|
var myExp = regexp.MustCompile(`\/?\*?@(?P<k>.*)\s*:\s*(?P<v>.*)\*?\/?`)
|
||||||
|
|
||||||
|
var myExp2 = regexp.MustCompile(`\/?\*?@(?P<k>.*?)\*?\/?$`)
|
||||||
|
|
||||||
|
for _, v := range a.List {
|
||||||
|
lines := strings.Split(v.Text, "\n")
|
||||||
|
for _, l := range lines {
|
||||||
|
m := myExp.FindStringSubmatch(l)
|
||||||
|
if m != nil {
|
||||||
|
ret[m[1]] = m[2]
|
||||||
|
} else {
|
||||||
|
m := myExp2.FindStringSubmatch(l)
|
||||||
|
if m != nil {
|
||||||
|
ret[m[1]] = "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Gofname string `yaml:"gofname"`
|
||||||
|
Goimpldir string `yaml:"goimpldir"`
|
||||||
|
Tsfname string `json:"tsfname"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config Config
|
||||||
|
|
||||||
|
func loadConfig() {
|
||||||
|
fname := flag.String("f", ".apigen.yaml", "File with config to load - defaults to '.apigen'")
|
||||||
|
flag.Parse()
|
||||||
|
bs, err := ioutil.ReadFile(*fname)
|
||||||
|
Err(err)
|
||||||
|
err = yaml.Unmarshal(bs, &config)
|
||||||
|
Err(err)
|
||||||
|
}
|
196
main.go
196
main.go
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"dc"
|
"dc"
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
|
@ -13,105 +12,17 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type API struct {
|
|
||||||
BasePath string `yaml:"basepath"`
|
|
||||||
Host string `yaml:"host"`
|
|
||||||
Types map[string]*APIType `yaml:"types"`
|
|
||||||
Methods map[string]*APIMethod `yaml:"methods"`
|
|
||||||
Namespace string
|
|
||||||
}
|
|
||||||
type APIField struct {
|
|
||||||
Type string `yaml:"type"`
|
|
||||||
Array bool `yaml:"array"`
|
|
||||||
Desc string `yaml:"desc"`
|
|
||||||
Map bool `yaml:"map"`
|
|
||||||
Mapkey string `yaml:"mapkey"`
|
|
||||||
Mapval string `yaml:"mapval"`
|
|
||||||
}
|
|
||||||
type APIType struct {
|
|
||||||
Name string
|
|
||||||
Desc string `yaml:"desc"`
|
|
||||||
Fields map[string]*APIField `yaml:"fields"`
|
|
||||||
Col string `yaml:"col"`
|
|
||||||
}
|
|
||||||
type APIMethod struct {
|
|
||||||
Desc string `yaml:"desc"`
|
|
||||||
Verb string `yaml:"verb"`
|
|
||||||
Path string `yaml:"path"`
|
|
||||||
Perm string `yaml:perm`
|
|
||||||
ReqType string `yaml:"reqtype"`
|
|
||||||
ResType string `yaml:"restype"`
|
|
||||||
Raw bool `yaml:"raw"`
|
|
||||||
}
|
|
||||||
|
|
||||||
var api API
|
var api API
|
||||||
|
|
||||||
var gofname string
|
//var httptestdir string
|
||||||
var goimplfdir string
|
|
||||||
var tsfname string
|
|
||||||
var httptestdir string
|
|
||||||
var tstypemapper map[string]string = make(map[string]string)
|
var tstypemapper map[string]string = make(map[string]string)
|
||||||
var knownMethods map[string]bool = make(map[string]bool)
|
var knownMethods map[string]bool = make(map[string]bool)
|
||||||
var httpMapper map[string]map[string]string = make(map[string]map[string]string)
|
var httpMapper map[string]map[string]string = make(map[string]map[string]string)
|
||||||
var packageName string = "main"
|
var packageName string = "main"
|
||||||
|
|
||||||
func manageComments(a *ast.CommentGroup) map[string]string {
|
|
||||||
ret := make(map[string]string)
|
|
||||||
if a == nil {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
var myExp = regexp.MustCompile(`@(?P<k>.*)\s*:\s*(?P<v>.*)`)
|
|
||||||
var myExp2 = regexp.MustCompile(`@(?P<k>.*?)$`)
|
|
||||||
for _, v := range a.List {
|
|
||||||
lines := strings.Split(v.Text, "\n")
|
|
||||||
for _, l := range lines {
|
|
||||||
m := myExp.FindStringSubmatch(l)
|
|
||||||
if m != nil {
|
|
||||||
ret[m[1]] = m[2]
|
|
||||||
} else {
|
|
||||||
m := myExp2.FindStringSubmatch(l)
|
|
||||||
if m != nil {
|
|
||||||
ret[m[1]] = "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func manageCommentsGroups(as []*ast.CommentGroup) map[string]string {
|
|
||||||
ret := make(map[string]string)
|
|
||||||
if as == nil {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
for _, a := range as {
|
|
||||||
var myExp = regexp.MustCompile(`@(?P<k>.*)\s*:\s*(?P<v>.*)`)
|
|
||||||
var myExp2 = regexp.MustCompile(`@(?P<k>.*?)$`)
|
|
||||||
for _, v := range a.List {
|
|
||||||
lines := strings.Split(v.Text, "\n")
|
|
||||||
for _, l := range lines {
|
|
||||||
m := myExp.FindStringSubmatch(l)
|
|
||||||
if m != nil {
|
|
||||||
ret[m[1]] = m[2]
|
|
||||||
} else {
|
|
||||||
m := myExp2.FindStringSubmatch(l)
|
|
||||||
if m != nil {
|
|
||||||
ret[m[1]] = "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func addStruct(a *ast.GenDecl) {
|
func addStruct(a *ast.GenDecl) {
|
||||||
md := manageComments(a.Doc)
|
md := manageComments(a.Doc)
|
||||||
if md["API"] == "" {
|
if md["API"] == "" {
|
||||||
|
@ -191,21 +102,22 @@ func addFunction(a *ast.FuncDecl) {
|
||||||
if md["API"] == "" {
|
if md["API"] == "" {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reqType := ""
|
reqType := &APIParamType{}
|
||||||
resType := ""
|
resType := &APIParamType{}
|
||||||
|
|
||||||
if len(a.Type.Params.List) > 1 {
|
if len(a.Type.Params.List) > 1 {
|
||||||
switch x := a.Type.Params.List[1].Type.(type) {
|
switch x := a.Type.Params.List[1].Type.(type) {
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
|
reqType.Ispointer = true
|
||||||
switch y := x.X.(type) {
|
switch y := x.X.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
reqType = y.Name
|
reqType.Typename = y.Name
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
reqType = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
reqType = x.Name
|
reqType.Typename = x.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,21 +125,22 @@ func addFunction(a *ast.FuncDecl) {
|
||||||
|
|
||||||
switch x := a.Type.Results.List[0].Type.(type) {
|
switch x := a.Type.Results.List[0].Type.(type) {
|
||||||
case *ast.StarExpr:
|
case *ast.StarExpr:
|
||||||
|
resType.Ispointer = true
|
||||||
switch y := x.X.(type) {
|
switch y := x.X.(type) {
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
resType = y.Name
|
resType.Typename = y.Name
|
||||||
case *ast.SelectorExpr:
|
case *ast.SelectorExpr:
|
||||||
resType = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.Ident:
|
case *ast.Ident:
|
||||||
resType = x.Name
|
resType.Typename = x.Name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if md["RAW"] == "true" {
|
if md["RAW"] == "true" {
|
||||||
reqType = md["REQ"]
|
reqType.Typename = md["REQ"]
|
||||||
resType = md["RES"]
|
resType.Typename = md["RES"]
|
||||||
}
|
}
|
||||||
|
|
||||||
verb := md["VERB"]
|
verb := md["VERB"]
|
||||||
|
@ -246,11 +159,21 @@ func addFunction(a *ast.FuncDecl) {
|
||||||
api.Methods[a.Name.Name] = &fn
|
api.Methods[a.Name.Name] = &fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func processGoServerOutput(f string, api *API) {
|
func processGoServerOutput(api *API) {
|
||||||
b := bytes.Buffer{}
|
|
||||||
if f == "" {
|
APIParamTypeToString := func(t *APIParamType) string {
|
||||||
f = gofname
|
ret := ""
|
||||||
|
if t.Ispointer {
|
||||||
|
ret = ret + "&"
|
||||||
}
|
}
|
||||||
|
ret = ret + t.Typename
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
|
||||||
|
f := config.Gofname
|
||||||
|
|
||||||
os.Remove(f)
|
os.Remove(f)
|
||||||
b.WriteString(fmt.Sprintf(`package %s
|
b.WriteString(fmt.Sprintf(`package %s
|
||||||
|
|
||||||
|
@ -345,7 +268,7 @@ ret.Perms["%s_%s"]="%s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
`, m.ReqType, k))
|
`, APIParamTypeToString(m.ReqType), k))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +282,7 @@ ret.Perms["%s_%s"]="%s"
|
||||||
func processTSClientOutput(f string, api *API) {
|
func processTSClientOutput(f string, api *API) {
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
if f == "" {
|
if f == "" {
|
||||||
f = tsfname
|
f = config.Tsfname
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString("//#region Base\n")
|
b.WriteString("//#region Base\n")
|
||||||
|
@ -377,6 +300,8 @@ export function GetAPIBase(): string{
|
||||||
|
|
||||||
let REGEX_DATE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)(Z|([+\-])(\d{2}):(\d{2}))$/
|
let REGEX_DATE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)(Z|([+\-])(\d{2}):(\d{2}))$/
|
||||||
|
|
||||||
|
type HTMLMethod = "GET" | "POST" | "PUT" | "DELETE" | "HEAD" | "TRACE"
|
||||||
|
|
||||||
async function Invoke(path: string, method: HTMLMethod, body?: any): Promise<Response> {
|
async function Invoke(path: string, method: HTMLMethod, body?: any): Promise<Response> {
|
||||||
let jbody = undefined
|
let jbody = undefined
|
||||||
let init = {method: method, mode: "cors", credentials: "include", withCredentials: true}
|
let init = {method: method, mode: "cors", credentials: "include", withCredentials: true}
|
||||||
|
@ -491,7 +416,7 @@ async function InvokeOk(path: string, method: HTMLMethod, body?: any): Promise<b
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//} else {
|
//} else {
|
||||||
b.WriteString(fmt.Sprintf("export async function %s(req:%s):Promise<%s>{\n", k, m.ReqType, m.ResType))
|
b.WriteString(fmt.Sprintf("export async function %s(req:%s):Promise<%s>{\n", k, m.ReqType.Typename, m.ResType.Typename))
|
||||||
b.WriteString(fmt.Sprintf("\treturn InvokeJSON(\"%s\",\"%s\",req)\n", m.Path, m.Verb))
|
b.WriteString(fmt.Sprintf("\treturn InvokeJSON(\"%s\",\"%s\",req)\n", m.Path, m.Verb))
|
||||||
b.WriteString(fmt.Sprintf("}\n\n"))
|
b.WriteString(fmt.Sprintf("}\n\n"))
|
||||||
//}
|
//}
|
||||||
|
@ -502,27 +427,28 @@ async function InvokeOk(path: string, method: HTMLMethod, body?: any): Promise<b
|
||||||
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
||||||
dc.Err(err)
|
dc.Err(err)
|
||||||
}
|
}
|
||||||
func processHTTPTestOutput(f string, api *API) {
|
|
||||||
|
|
||||||
for k, m := range api.Methods {
|
//func processHTTPTestOutput(f string, api *API) {
|
||||||
fname := path.Join(httptestdir, k+".http")
|
//
|
||||||
if dc.FileExists(fname) {
|
// for k, m := range api.Methods {
|
||||||
continue
|
// fname := path.Join(httptestdir, k+".http")
|
||||||
}
|
// if dc.FileExists(fname) {
|
||||||
b := bytes.Buffer{}
|
// continue
|
||||||
b.WriteString(fmt.Sprintf(`%s{{BASEURL}}%s
|
// }
|
||||||
Content-Type: application/json
|
// b := bytes.Buffer{}
|
||||||
Cookie: dc={{COOKIE}}
|
// b.WriteString(fmt.Sprintf(`%s{{BASEURL}}%s
|
||||||
|
//Content-Type: application/json
|
||||||
{}
|
//Cookie: dc={{COOKIE}}
|
||||||
|
//
|
||||||
###
|
//{}
|
||||||
`, m.Verb, api.BasePath+m.Path))
|
//
|
||||||
|
//###
|
||||||
err := ioutil.WriteFile(fname, b.Bytes(), 0600)
|
//`, m.Verb, api.BasePath+m.Path))
|
||||||
dc.Err(err)
|
//
|
||||||
}
|
// err := ioutil.WriteFile(fname, b.Bytes(), 0600)
|
||||||
}
|
// dc.Err(err)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
func mapHttp(api *API) {
|
func mapHttp(api *API) {
|
||||||
for k, v := range api.Methods {
|
for k, v := range api.Methods {
|
||||||
|
@ -536,10 +462,8 @@ func mapHttp(api *API) {
|
||||||
}
|
}
|
||||||
func process(api *API) {
|
func process(api *API) {
|
||||||
mapHttp(api)
|
mapHttp(api)
|
||||||
processGoServerOutput(gofname, api)
|
processGoServerOutput(api)
|
||||||
//processGoMongoOutput(strings.Replace(gofname, ".go", "_mongo.go", 1), api)
|
|
||||||
processTSClientOutput("", api)
|
processTSClientOutput("", api)
|
||||||
processHTTPTestOutput("", api)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func load() {
|
func load() {
|
||||||
|
@ -549,7 +473,7 @@ func load() {
|
||||||
|
|
||||||
fset := token.NewFileSet() // positions are relative to fset
|
fset := token.NewFileSet() // positions are relative to fset
|
||||||
|
|
||||||
f, err := parser.ParseDir(fset, goimplfdir, nil, parser.ParseComments)
|
f, err := parser.ParseDir(fset, config.Goimpldir, nil, parser.ParseComments)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -596,6 +520,8 @@ func load() {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
|
loadConfig()
|
||||||
|
|
||||||
tstypemapper["time.Time"] = "Date"
|
tstypemapper["time.Time"] = "Date"
|
||||||
tstypemapper["primitive.ObjectID"] = "string"
|
tstypemapper["primitive.ObjectID"] = "string"
|
||||||
tstypemapper["time.Duration"] = "Date"
|
tstypemapper["time.Duration"] = "Date"
|
||||||
|
@ -612,13 +538,7 @@ func main() {
|
||||||
tstypemapper["interface{}"] = "any"
|
tstypemapper["interface{}"] = "any"
|
||||||
tstypemapper["bson.M"] = "any"
|
tstypemapper["bson.M"] = "any"
|
||||||
|
|
||||||
flag.StringVar(&gofname, "g", "api.go", "Go API Handle File")
|
os.Remove(config.Gofname)
|
||||||
flag.StringVar(&goimplfdir, "I", "src", "Go API Impl Dir")
|
|
||||||
flag.StringVar(&tsfname, "t", "api.ts", "TS File")
|
|
||||||
flag.StringVar(&httptestdir, "h", "test.http", "HTTP Test Dir")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
os.Remove(gofname)
|
|
||||||
load()
|
load()
|
||||||
process(&api)
|
process(&api)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package goapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*@API*/
|
||||||
|
type ARequestStruct struct {
|
||||||
|
A *string
|
||||||
|
B int64
|
||||||
|
C time.Time
|
||||||
|
D *string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@API*/
|
||||||
|
type AResponseStruct struct {
|
||||||
|
A string
|
||||||
|
B int64
|
||||||
|
C time.Time
|
||||||
|
D *string
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@API
|
||||||
|
PATH: /someapi
|
||||||
|
*/
|
||||||
|
func SomeAPI(ctx context.Context, a *ARequestStruct) (*AResponseStruct, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
BasePath string `yaml:"basepath"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Types map[string]*APIType `yaml:"types"`
|
||||||
|
Methods map[string]*APIMethod `yaml:"methods"`
|
||||||
|
Namespace string
|
||||||
|
}
|
||||||
|
type APIField struct {
|
||||||
|
Type string `yaml:"type"`
|
||||||
|
Array bool `yaml:"array"`
|
||||||
|
Desc string `yaml:"desc"`
|
||||||
|
Map bool `yaml:"map"`
|
||||||
|
Mapkey string `yaml:"mapkey"`
|
||||||
|
Mapval string `yaml:"mapval"`
|
||||||
|
}
|
||||||
|
type APIType struct {
|
||||||
|
Name string
|
||||||
|
Desc string `yaml:"desc"`
|
||||||
|
Fields map[string]*APIField `yaml:"fields"`
|
||||||
|
Col string `yaml:"col"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIParamType struct {
|
||||||
|
Typename string
|
||||||
|
Ispointer bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type APIMethod struct {
|
||||||
|
Desc string `yaml:"desc"`
|
||||||
|
Verb string `yaml:"verb"`
|
||||||
|
Path string `yaml:"path"`
|
||||||
|
Perm string `yaml:perm`
|
||||||
|
ReqType *APIParamType
|
||||||
|
ResType *APIParamType
|
||||||
|
Raw bool `yaml:"raw"`
|
||||||
|
}
|
Loading…
Reference in New Issue