multiple improvements
parent
517aa43668
commit
fa16e16e9b
|
@ -0,0 +1,13 @@
|
||||||
|
module go.digitalcircle.com.br/tools/apigen
|
||||||
|
|
||||||
|
go 1.15
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/alecthomas/kong v0.2.12
|
||||||
|
github.com/bxcodec/faker/v3 v3.5.0 // indirect
|
||||||
|
github.com/fatih/structtag v1.2.0
|
||||||
|
github.com/pkg/errors v0.9.1
|
||||||
|
github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760
|
||||||
|
go.digitalcircle.com.br/golib/base v0.0.0-20210124165830-341c1d300435
|
||||||
|
gopkg.in/yaml.v2 v2.4.0
|
||||||
|
)
|
|
@ -0,0 +1,22 @@
|
||||||
|
github.com/alecthomas/kong v0.2.12 h1:X3kkCOXGUNzLmiu+nQtoxWqj4U2a39MpSJR3QdQXOwI=
|
||||||
|
github.com/alecthomas/kong v0.2.12/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=
|
||||||
|
github.com/bxcodec/faker v1.5.0 h1:RIWOeAcM3ZHye1i8bQtHU2LfNOaLmHuRiCo60mNMOcQ=
|
||||||
|
github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA=
|
||||||
|
github.com/bxcodec/faker/v3 v3.5.0 h1:Rahy6dwbd6up0wbwbV7dFyQb+jmdC51kpATuUdnzfMg=
|
||||||
|
github.com/bxcodec/faker/v3 v3.5.0/go.mod h1:gF31YgnMSMKgkvl+fyEo1xuSMbEuieyqfeslGYFjneM=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||||
|
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760 h1:i+m5+M2renk/OmDHvzRjlBQXm+5X6WR6xPjWfHNzvcM=
|
||||||
|
github.com/yuin/stagparser v0.0.0-20181218160030-e10a81132760/go.mod h1:+qbo7cNcx8dT/77C41x4MZbasDrLuUDI/04ZGR/7IqM=
|
||||||
|
go.digitalcircle.com.br/golib/base v0.0.0-20210124165830-341c1d300435 h1:XVnDH9egiX/iTc59738ZozUZHGahb494y52oxmLJYN4=
|
||||||
|
go.digitalcircle.com.br/golib/base v0.0.0-20210124165830-341c1d300435/go.mod h1:8BeArwDJW81pf5Fhchrs/Hh2YSg0FMnNeaV+j1kUEyM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
@ -1,25 +1,19 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
//type Config struct {
|
||||||
"flag"
|
// Gofname string `yaml:"gofname"`
|
||||||
"gopkg.in/yaml.v2"
|
// Goimpldir string `yaml:"goimpldir"`
|
||||||
"io/ioutil"
|
// Tsfname string `json:"tsfname"`
|
||||||
)
|
// Goclifname string `json:"goclifname"`
|
||||||
|
//}
|
||||||
type Config struct {
|
//
|
||||||
Gofname string `yaml:"gofname"`
|
//var config Config
|
||||||
Goimpldir string `yaml:"goimpldir"`
|
//
|
||||||
Tsfname string `json:"tsfname"`
|
//func loadConfig() {
|
||||||
Goclifname string `json:"goclifname"`
|
// fname := flag.String("f", ".apigen.yaml", "File with config to load - defaults to '.apigen'")
|
||||||
}
|
// flag.Parse()
|
||||||
|
// bs, err := ioutil.ReadFile(*fname)
|
||||||
var config Config
|
// Err(err)
|
||||||
|
// err = yaml.Unmarshal(bs, &config)
|
||||||
func loadConfig() {
|
// Err(err)
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,265 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/structtag"
|
||||||
|
"go/token"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var api API
|
||||||
|
|
||||||
|
func addStruct(a *ast.GenDecl) {
|
||||||
|
md := manageComments(a.Doc)
|
||||||
|
if md["API"] == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tp := APIType{
|
||||||
|
Name: "",
|
||||||
|
Desc: "",
|
||||||
|
Fields: make(map[string]*APIField),
|
||||||
|
Col: md["COL"],
|
||||||
|
}
|
||||||
|
tp.Name = a.Specs[0].(*ast.TypeSpec).Name.Name
|
||||||
|
log.Printf("Type:" + tp.Name)
|
||||||
|
for _, v := range a.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List {
|
||||||
|
tp.Fields[v.Names[0].Name] = &APIField{}
|
||||||
|
tp.Fields[v.Names[0].Name].Tags = make(map[string]APIFieldTag)
|
||||||
|
|
||||||
|
switch x := v.Type.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = x.Name
|
||||||
|
case *ast.ArrayType:
|
||||||
|
switch z := x.Elt.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = z.Name
|
||||||
|
tp.Fields[v.Names[0].Name].Array = true
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
||||||
|
tp.Fields[v.Names[0].Name].Array = true
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
||||||
|
tp.Fields[v.Names[0].Name].Array = true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.StarExpr:
|
||||||
|
switch y := x.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = y.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
switch z := y.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = z.Name + "." + y.Sel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
||||||
|
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
|
||||||
|
switch z := x.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = z.Name + "." + x.Sel.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.MapType:
|
||||||
|
|
||||||
|
switch z := x.Value.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = ""
|
||||||
|
tp.Fields[v.Names[0].Name].Mapkey = x.Key.(*ast.Ident).Name
|
||||||
|
tp.Fields[v.Names[0].Name].Mapval = z.Name
|
||||||
|
tp.Fields[v.Names[0].Name].Map = true
|
||||||
|
case *ast.InterfaceType:
|
||||||
|
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
||||||
|
tp.Fields[v.Names[0].Name].Array = true
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
log.Printf("%#v", x)
|
||||||
|
}
|
||||||
|
if v.Tag != nil {
|
||||||
|
tgstr := strings.ReplaceAll(v.Tag.Value, "`", "")
|
||||||
|
tg, err := structtag.Parse(tgstr)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
for _, tgv := range tg.Keys() {
|
||||||
|
atg, err := tg.Get(tgv)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
tp.Fields[v.Names[0].Name].Tags[tgv] = APIFieldTag{
|
||||||
|
Key: tgv,
|
||||||
|
Name: atg.Name,
|
||||||
|
Opts: atg.Options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Printf("#%v", tg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
api.Types[tp.Name] = &tp
|
||||||
|
}
|
||||||
|
|
||||||
|
func addFunction(a *ast.FuncDecl) {
|
||||||
|
md := manageComments(a.Doc)
|
||||||
|
|
||||||
|
if md["API"] == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reqType := &APIParamType{}
|
||||||
|
resType := &APIParamType{}
|
||||||
|
|
||||||
|
if len(a.Type.Params.List) > 1 {
|
||||||
|
switch x := a.Type.Params.List[1].Type.(type) {
|
||||||
|
case *ast.StarExpr:
|
||||||
|
reqType.Ispointer = true
|
||||||
|
switch y := x.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
reqType.Typename = y.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
|
}
|
||||||
|
case *ast.ArrayType:
|
||||||
|
reqType.IsArray = true
|
||||||
|
switch y := x.Elt.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
reqType.Typename = y.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
reqType.Ispointer = true
|
||||||
|
switch z := y.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
reqType.Typename = z.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
reqType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.Ident:
|
||||||
|
reqType.Typename = x.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Type.Results != nil && len(a.Type.Results.List) > 0 {
|
||||||
|
|
||||||
|
switch x := a.Type.Results.List[0].Type.(type) {
|
||||||
|
case *ast.StarExpr:
|
||||||
|
resType.Ispointer = true
|
||||||
|
switch y := x.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
resType.Typename = y.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
|
}
|
||||||
|
case *ast.ArrayType:
|
||||||
|
resType.IsArray = true
|
||||||
|
switch y := x.Elt.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
resType.Typename = y.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
||||||
|
case *ast.StarExpr:
|
||||||
|
resType.Ispointer = true
|
||||||
|
switch z := y.X.(type) {
|
||||||
|
case *ast.Ident:
|
||||||
|
resType.Typename = z.Name
|
||||||
|
case *ast.SelectorExpr:
|
||||||
|
resType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if md["RAW"] == "true" {
|
||||||
|
reqType.Typename = md["REQ"]
|
||||||
|
resType.Typename = md["RES"]
|
||||||
|
}
|
||||||
|
|
||||||
|
verb := md["VERB"]
|
||||||
|
if verb == "" {
|
||||||
|
verb = http.MethodPost
|
||||||
|
}
|
||||||
|
fn := APIMethod{
|
||||||
|
Desc: a.Name.Name,
|
||||||
|
Verb: verb,
|
||||||
|
Path: md["PATH"],
|
||||||
|
Perm: md["PERM"],
|
||||||
|
ReqType: reqType,
|
||||||
|
ResType: resType,
|
||||||
|
Raw: md["RAW"] == "true",
|
||||||
|
}
|
||||||
|
api.Methods[a.Name.Name] = &fn
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func load(src string) error {
|
||||||
|
|
||||||
|
api.Types = (make(map[string]*APIType))
|
||||||
|
api.Methods = (make(map[string]*APIMethod))
|
||||||
|
|
||||||
|
fset := token.NewFileSet() // positions are relative to fset
|
||||||
|
|
||||||
|
f, err := parser.ParseDir(fset, src, nil, parser.ParseComments)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range f {
|
||||||
|
// Print the AST.
|
||||||
|
ast.Inspect(v, func(n ast.Node) bool {
|
||||||
|
|
||||||
|
switch x := n.(type) {
|
||||||
|
|
||||||
|
case *ast.GenDecl:
|
||||||
|
if x.Tok == token.TYPE {
|
||||||
|
addStruct(x)
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case *ast.File:
|
||||||
|
c := manageCommentsGroups(x.Comments)
|
||||||
|
log.Printf("%+v", c)
|
||||||
|
case *ast.FuncDecl:
|
||||||
|
addFunction(x)
|
||||||
|
case *ast.ValueSpec:
|
||||||
|
if x.Names[0].Name == "BASEPATH" {
|
||||||
|
api.BasePath = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
||||||
|
}
|
||||||
|
if x.Names[0].Name == "NAMESPACE" {
|
||||||
|
api.Namespace = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
||||||
|
}
|
||||||
|
log.Printf("%#v", x)
|
||||||
|
case *ast.Package:
|
||||||
|
packageName = x.Name
|
||||||
|
default:
|
||||||
|
//log.Printf("%#v", x)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range api.Methods {
|
||||||
|
pathmap, ok := httpMapper[v.Path]
|
||||||
|
if !ok {
|
||||||
|
httpMapper[v.Path] = make(map[string]string)
|
||||||
|
pathmap = httpMapper[v.Path]
|
||||||
|
}
|
||||||
|
pathmap[v.Verb] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
348
src/main.go
348
src/main.go
|
@ -1,282 +1,102 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go/ast"
|
"github.com/alecthomas/kong"
|
||||||
"go/parser"
|
"github.com/pkg/errors"
|
||||||
"go/token"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var api API
|
|
||||||
|
|
||||||
//var httptestdir string
|
|
||||||
var tstypemapper map[string]string = make(map[string]string)
|
|
||||||
var exceptionaltypemapper 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 addStruct(a *ast.GenDecl) {
|
var CLI struct {
|
||||||
md := manageComments(a.Doc)
|
Yaml struct {
|
||||||
if md["API"] == "" {
|
Src string `arg help:"Source Dir"`
|
||||||
return
|
Fname string `arg help:"File to be generated"`
|
||||||
}
|
} `cmd help:"Gens YAML metamodel"`
|
||||||
tp := APIType{
|
Goserver struct {
|
||||||
Name: "",
|
Src string `arg help:"Source Dir"`
|
||||||
Desc: "",
|
} `cmd help:"Gens GO Server impl"`
|
||||||
Fields: make(map[string]*APIField),
|
Gocli struct {
|
||||||
Col: md["COL"],
|
Src string `arg help:"Source Dir"`
|
||||||
}
|
Dst string `arg help:"Dst file"`
|
||||||
tp.Name = a.Specs[0].(*ast.TypeSpec).Name.Name
|
} `cmd help:"Gens Go Cli impl"`
|
||||||
log.Printf("Type:" + tp.Name)
|
Ts struct {
|
||||||
for _, v := range a.Specs[0].(*ast.TypeSpec).Type.(*ast.StructType).Fields.List {
|
Src string `arg help:"Source Dir"`
|
||||||
tp.Fields[v.Names[0].Name] = &APIField{}
|
Dst string `arg help:"Dst file"`
|
||||||
switch x := v.Type.(type) {
|
} `cmd help:"Gens Typescript Cli impl"`
|
||||||
case *ast.Ident:
|
Http struct {
|
||||||
tp.Fields[v.Names[0].Name].Type = x.Name
|
Src string `arg help:"Source Dir"`
|
||||||
case *ast.ArrayType:
|
Dst string `arg help:"Dst file"`
|
||||||
switch z := x.Elt.(type) {
|
} `cmd help:"Gens Http call impl"`
|
||||||
case *ast.Ident:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = z.Name
|
|
||||||
tp.Fields[v.Names[0].Name].Array = true
|
|
||||||
case *ast.InterfaceType:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
||||||
tp.Fields[v.Names[0].Name].Array = true
|
|
||||||
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
|
||||||
tp.Fields[v.Names[0].Name].Array = true
|
|
||||||
}
|
|
||||||
|
|
||||||
case *ast.StarExpr:
|
|
||||||
switch y := x.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = y.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
switch z := y.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = z.Name + "." + y.Sel.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.InterfaceType:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
||||||
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
|
|
||||||
switch z := x.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = z.Name + "." + x.Sel.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
case *ast.MapType:
|
|
||||||
|
|
||||||
switch z := x.Value.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = ""
|
|
||||||
tp.Fields[v.Names[0].Name].Mapkey = x.Key.(*ast.Ident).Name
|
|
||||||
tp.Fields[v.Names[0].Name].Mapval = z.Name
|
|
||||||
tp.Fields[v.Names[0].Name].Map = true
|
|
||||||
case *ast.InterfaceType:
|
|
||||||
tp.Fields[v.Names[0].Name].Type = "interface{}"
|
|
||||||
tp.Fields[v.Names[0].Name].Array = true
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Printf("%#v", x)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
api.Types[tp.Name] = &tp
|
|
||||||
}
|
|
||||||
|
|
||||||
func addFunction(a *ast.FuncDecl) {
|
|
||||||
md := manageComments(a.Doc)
|
|
||||||
|
|
||||||
if md["API"] == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reqType := &APIParamType{}
|
|
||||||
resType := &APIParamType{}
|
|
||||||
|
|
||||||
if len(a.Type.Params.List) > 1 {
|
|
||||||
switch x := a.Type.Params.List[1].Type.(type) {
|
|
||||||
case *ast.StarExpr:
|
|
||||||
reqType.Ispointer = true
|
|
||||||
switch y := x.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
reqType.Typename = y.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
|
||||||
}
|
|
||||||
case *ast.ArrayType:
|
|
||||||
reqType.IsArray = true
|
|
||||||
switch y := x.Elt.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
reqType.Typename = y.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
|
||||||
case *ast.StarExpr:
|
|
||||||
reqType.Ispointer = true
|
|
||||||
switch z := y.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
reqType.Typename = z.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
reqType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case *ast.Ident:
|
|
||||||
reqType.Typename = x.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Type.Results != nil && len(a.Type.Results.List) > 0 {
|
|
||||||
|
|
||||||
switch x := a.Type.Results.List[0].Type.(type) {
|
|
||||||
case *ast.StarExpr:
|
|
||||||
resType.Ispointer = true
|
|
||||||
switch y := x.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
resType.Typename = y.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
|
||||||
}
|
|
||||||
case *ast.ArrayType:
|
|
||||||
resType.IsArray = true
|
|
||||||
switch y := x.Elt.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
resType.Typename = y.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name
|
|
||||||
case *ast.StarExpr:
|
|
||||||
resType.Ispointer = true
|
|
||||||
switch z := y.X.(type) {
|
|
||||||
case *ast.Ident:
|
|
||||||
resType.Typename = z.Name
|
|
||||||
case *ast.SelectorExpr:
|
|
||||||
resType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if md["RAW"] == "true" {
|
|
||||||
reqType.Typename = md["REQ"]
|
|
||||||
resType.Typename = md["RES"]
|
|
||||||
}
|
|
||||||
|
|
||||||
verb := md["VERB"]
|
|
||||||
if verb == "" {
|
|
||||||
verb = http.MethodPost
|
|
||||||
}
|
|
||||||
fn := APIMethod{
|
|
||||||
Desc: a.Name.Name,
|
|
||||||
Verb: verb,
|
|
||||||
Path: md["PATH"],
|
|
||||||
Perm: md["PERM"],
|
|
||||||
ReqType: reqType,
|
|
||||||
ResType: resType,
|
|
||||||
Raw: md["RAW"] == "true",
|
|
||||||
}
|
|
||||||
api.Methods[a.Name.Name] = &fn
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapHttp(api *API) {
|
|
||||||
for k, v := range api.Methods {
|
|
||||||
pathmap, ok := httpMapper[v.Path]
|
|
||||||
if !ok {
|
|
||||||
httpMapper[v.Path] = make(map[string]string)
|
|
||||||
pathmap = httpMapper[v.Path]
|
|
||||||
}
|
|
||||||
pathmap[v.Verb] = k
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func process(api *API) {
|
|
||||||
mapHttp(api)
|
|
||||||
processGoServerOutput(api)
|
|
||||||
processTSClientOutput("", api)
|
|
||||||
processGoClientOutput(api)
|
|
||||||
}
|
|
||||||
|
|
||||||
func load() {
|
|
||||||
|
|
||||||
api.Types = (make(map[string]*APIType))
|
|
||||||
api.Methods = (make(map[string]*APIMethod))
|
|
||||||
|
|
||||||
fset := token.NewFileSet() // positions are relative to fset
|
|
||||||
|
|
||||||
f, err := parser.ParseDir(fset, config.Goimpldir, nil, parser.ParseComments)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range f {
|
|
||||||
// Print the AST.
|
|
||||||
ast.Inspect(v, func(n ast.Node) bool {
|
|
||||||
|
|
||||||
switch x := n.(type) {
|
|
||||||
|
|
||||||
case *ast.GenDecl:
|
|
||||||
if x.Tok == token.TYPE {
|
|
||||||
addStruct(x)
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
case *ast.File:
|
|
||||||
c := manageCommentsGroups(x.Comments)
|
|
||||||
log.Printf("%+v", c)
|
|
||||||
case *ast.FuncDecl:
|
|
||||||
addFunction(x)
|
|
||||||
case *ast.ValueSpec:
|
|
||||||
if x.Names[0].Name == "BASEPATH" {
|
|
||||||
api.BasePath = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
|
||||||
}
|
|
||||||
if x.Names[0].Name == "NAMESPACE" {
|
|
||||||
api.Namespace = strings.Replace(x.Values[0].(*ast.BasicLit).Value, "\"", "", -1)
|
|
||||||
}
|
|
||||||
log.Printf("%#v", x)
|
|
||||||
case *ast.Package:
|
|
||||||
packageName = x.Name
|
|
||||||
default:
|
|
||||||
//log.Printf("%#v", x)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
loadConfig()
|
var processor func() error
|
||||||
|
kong.ConfigureHelp(kong.HelpOptions{
|
||||||
|
NoAppSummary: false,
|
||||||
|
Summary: true,
|
||||||
|
Compact: true,
|
||||||
|
Tree: true,
|
||||||
|
Indenter: nil,
|
||||||
|
})
|
||||||
|
ctx := kong.Parse(&CLI)
|
||||||
|
var err error
|
||||||
|
var src string
|
||||||
|
switch ctx.Command() {
|
||||||
|
case "yaml <src> <fname>":
|
||||||
|
log.Printf("Gens YAML")
|
||||||
|
src = CLI.Yaml.Src
|
||||||
|
processor = func() error {
|
||||||
|
return processYaml(CLI.Yaml.Fname, nil)
|
||||||
|
}
|
||||||
|
case "goserver <src>":
|
||||||
|
log.Printf("Gen GO Server")
|
||||||
|
src = CLI.Goserver.Src
|
||||||
|
processor = func() error {
|
||||||
|
return processGoServerOutput(CLI.Goserver.Src + "/apigen.go")
|
||||||
|
}
|
||||||
|
case "gocli <src> <dst>":
|
||||||
|
log.Printf("Gen GO Client")
|
||||||
|
src = CLI.Gocli.Src
|
||||||
|
processor = func() error {
|
||||||
|
return processGoClientOutput(CLI.Gocli.Dst)
|
||||||
|
}
|
||||||
|
case "ts <src> <dst>":
|
||||||
|
log.Printf("Gen TS Client")
|
||||||
|
src = CLI.Ts.Src
|
||||||
|
processor = func() error {
|
||||||
|
return processTSClientOutput(CLI.Ts.Dst)
|
||||||
|
}
|
||||||
|
case "http <src> <dst>":
|
||||||
|
log.Printf("Gen Http Client")
|
||||||
|
src = CLI.Http.Src
|
||||||
|
processor = func() error {
|
||||||
|
return processHttpCallOut(CLI.Http.Dst)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
err = errors.New("unknown option")
|
||||||
|
}
|
||||||
|
|
||||||
exceptionaltypemapper["[]byte"] = "string"
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = load(src)
|
||||||
|
|
||||||
tstypemapper["time.Time"] = "Date"
|
if err != nil {
|
||||||
tstypemapper["primitive.ObjectID"] = "string"
|
panic(err)
|
||||||
tstypemapper["time.Duration"] = "Date"
|
}
|
||||||
tstypemapper["int"] = "number"
|
err = processor()
|
||||||
tstypemapper["int32"] = "number"
|
|
||||||
tstypemapper["int64"] = "number"
|
|
||||||
tstypemapper["float"] = "number"
|
|
||||||
tstypemapper["float64"] = "number"
|
|
||||||
tstypemapper["uint8"] = "number"
|
|
||||||
tstypemapper["uint16"] = "number"
|
|
||||||
tstypemapper["uint32"] = "number"
|
|
||||||
tstypemapper["error"] = "Error"
|
|
||||||
tstypemapper["bool"] = "boolean"
|
|
||||||
tstypemapper["interface{}"] = "any"
|
|
||||||
tstypemapper["bson.M"] = "any"
|
|
||||||
|
|
||||||
os.Remove(config.Gofname)
|
if err != nil {
|
||||||
load()
|
panic(err)
|
||||||
process(&api)
|
}
|
||||||
|
//loadConfig()
|
||||||
|
//
|
||||||
|
|
||||||
|
//os.Remove(config.Gofname)
|
||||||
|
|
||||||
|
//process(&api)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func processGoClientOutput(api *API) {
|
func processGoClientOutput(f string) error {
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
f := config.Goclifname
|
|
||||||
fparts := strings.Split(f, "/")
|
fparts := strings.Split(f, "/")
|
||||||
pkg := fparts[len(fparts)-2]
|
pkg := fparts[len(fparts)-2]
|
||||||
|
|
||||||
|
@ -109,7 +109,5 @@ func invoke(m string, path string, bodyo interface{}) (*json.Decoder, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
||||||
if err != nil {
|
return err
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,15 +7,26 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func processGoServerOutput(api *API) {
|
func processGoServerOutput(f string) error {
|
||||||
|
|
||||||
|
strKeys := make([]string, 0)
|
||||||
|
for k, _ := range api.Types {
|
||||||
|
strKeys = append(strKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(strKeys)
|
||||||
|
|
||||||
|
strMKeys := make([]string, 0)
|
||||||
|
for k, _ := range api.Methods {
|
||||||
|
strKeys = append(strMKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(strMKeys)
|
||||||
|
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
|
|
||||||
f := config.Gofname
|
|
||||||
|
|
||||||
os.Remove(f)
|
os.Remove(f)
|
||||||
b.WriteString(fmt.Sprintf(`package %s
|
b.WriteString(fmt.Sprintf(`package %s
|
||||||
|
|
||||||
|
@ -47,7 +58,8 @@ func Init() API{
|
||||||
Perms: make(map[string]string),
|
Perms: make(map[string]string),
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
for _, m := range api.Methods {
|
for _, k := range strMKeys {
|
||||||
|
m := api.Methods[k]
|
||||||
if m.Perm != "" {
|
if m.Perm != "" {
|
||||||
b.WriteString(fmt.Sprintf(`
|
b.WriteString(fmt.Sprintf(`
|
||||||
ret.Perms["%s_%s"]="%s"
|
ret.Perms["%s_%s"]="%s"
|
||||||
|
@ -57,12 +69,25 @@ ret.Perms["%s_%s"]="%s"
|
||||||
|
|
||||||
b.WriteString("\n\n")
|
b.WriteString("\n\n")
|
||||||
|
|
||||||
for p, mv := range httpMapper {
|
sortedMapper := make([]string, 0)
|
||||||
|
for k, _ := range httpMapper {
|
||||||
b.WriteString(fmt.Sprintf(" mux.HandleFunc(\"%s\",func(w http.ResponseWriter, r *http.Request) {\n", strings.Replace(p, "//", "/", -1)))
|
sortedMapper = append(sortedMapper, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedMapper)
|
||||||
|
for _, p := range sortedMapper {
|
||||||
|
mv := httpMapper[p]
|
||||||
|
b.WriteString(fmt.Sprintf(" mux.HandleFunc(\"%s\",func(w http.ResponseWriter, r *http.Request) {\n",
|
||||||
|
strings.Replace(p, "//", "/", -1)))
|
||||||
b.WriteString(" switch r.Method{\n")
|
b.WriteString(" switch r.Method{\n")
|
||||||
|
|
||||||
for v, id := range mv {
|
sorteVerbs := make([]string, 0)
|
||||||
|
for k, _ := range mv {
|
||||||
|
sorteVerbs = append(sorteVerbs, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sorteVerbs)
|
||||||
|
|
||||||
|
for _, v := range sorteVerbs {
|
||||||
|
id := mv[v]
|
||||||
|
|
||||||
b.WriteString(fmt.Sprintf(" case \"%s\":", v))
|
b.WriteString(fmt.Sprintf(" case \"%s\":", v))
|
||||||
if api.Methods[id].Raw {
|
if api.Methods[id].Raw {
|
||||||
|
@ -116,10 +141,11 @@ ret.Perms["%s_%s"]="%s"
|
||||||
|
|
||||||
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return err
|
||||||
}
|
}
|
||||||
cmd := exec.Command("/bin/sh", "-c", "go fmt "+f)
|
cmd := exec.Command("/bin/sh", "-c", "go fmt "+f)
|
||||||
bs, err := cmd.Output()
|
bs, err := cmd.Output()
|
||||||
//dc.Err(err)
|
//dc.Err(err)
|
||||||
dc.Log(string(bs))
|
dc.Log(string(bs))
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func typeToJson(tn string) interface{} {
|
||||||
|
|
||||||
|
rootmap := make(map[string]interface{})
|
||||||
|
|
||||||
|
td, ok := api.Types[tn]
|
||||||
|
if !ok {
|
||||||
|
if tn == "string" {
|
||||||
|
return "A STRING VALUE"
|
||||||
|
}
|
||||||
|
if tn == "bool" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(strings.ToLower(tn), "int") {
|
||||||
|
return 123456
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(strings.ToLower(tn), "float") {
|
||||||
|
return 123.456
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range td.Fields {
|
||||||
|
tg, ok := v.Tags["json"]
|
||||||
|
fname := strings.ToLower(k)
|
||||||
|
if ok {
|
||||||
|
fname = tg.Name
|
||||||
|
}
|
||||||
|
if v.Map {
|
||||||
|
submapval := typeToJson(v.Mapval)
|
||||||
|
submap := make(map[string]interface{})
|
||||||
|
submap["a"] = submapval
|
||||||
|
submap["b"] = submapval
|
||||||
|
submap["c"] = submapval
|
||||||
|
rootmap[fname] = submap
|
||||||
|
|
||||||
|
} else {
|
||||||
|
submapval := typeToJson(v.Type)
|
||||||
|
if v.Array {
|
||||||
|
submap := make([]interface{}, 0)
|
||||||
|
submap = append(submap, submapval)
|
||||||
|
submap = append(submap, submapval)
|
||||||
|
submap = append(submap, submapval)
|
||||||
|
rootmap[fname] = submap
|
||||||
|
} else {
|
||||||
|
rootmap[fname] = submapval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rootmap
|
||||||
|
}
|
||||||
|
|
||||||
|
func typeToJsonStr(tn string) string {
|
||||||
|
o := typeToJson(tn)
|
||||||
|
bs, err := json.MarshalIndent(o, "", "\t")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return string(bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func processHttpCallOut(f string) error {
|
||||||
|
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
|
||||||
|
sortedMethods := make([]string, 0)
|
||||||
|
for k, _ := range api.Methods {
|
||||||
|
sortedMethods = append(sortedMethods, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedMethods)
|
||||||
|
|
||||||
|
for _, k := range sortedMethods {
|
||||||
|
m := api.Methods[k]
|
||||||
|
tj := typeToJsonStr(m.ReqType.Typename)
|
||||||
|
b.WriteString("###\n")
|
||||||
|
if m.Desc != "" {
|
||||||
|
b.WriteString(fmt.Sprintf("#%s", strings.Replace(m.Desc, "\n", "\n#", -1)))
|
||||||
|
}
|
||||||
|
b.WriteString(fmt.Sprintf("\n"))
|
||||||
|
b.WriteString(fmt.Sprintf(m.Verb + " https://host/basepath" + m.Path + "\n"))
|
||||||
|
b.WriteString("Content-Type: application/json\n")
|
||||||
|
b.WriteString("Cookie: dc=<MYCOOKIE>\n\n")
|
||||||
|
b.WriteString(tj)
|
||||||
|
b.WriteString("\n\n")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -8,7 +8,27 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func processTSClientOutput(f string, api *API) {
|
func processTSClientOutput(f string) error {
|
||||||
|
|
||||||
|
var tstypemapper map[string]string = make(map[string]string)
|
||||||
|
var exceptionaltypemapper map[string]string = make(map[string]string)
|
||||||
|
exceptionaltypemapper["[]byte"] = "string"
|
||||||
|
|
||||||
|
tstypemapper["time.Time"] = "Date"
|
||||||
|
tstypemapper["primitive.ObjectID"] = "string"
|
||||||
|
tstypemapper["time.Duration"] = "Date"
|
||||||
|
tstypemapper["int"] = "number"
|
||||||
|
tstypemapper["int32"] = "number"
|
||||||
|
tstypemapper["int64"] = "number"
|
||||||
|
tstypemapper["float"] = "number"
|
||||||
|
tstypemapper["float64"] = "number"
|
||||||
|
tstypemapper["uint8"] = "number"
|
||||||
|
tstypemapper["uint16"] = "number"
|
||||||
|
tstypemapper["uint32"] = "number"
|
||||||
|
tstypemapper["error"] = "Error"
|
||||||
|
tstypemapper["bool"] = "boolean"
|
||||||
|
tstypemapper["interface{}"] = "any"
|
||||||
|
tstypemapper["bson.M"] = "any"
|
||||||
|
|
||||||
_typeName := func(m *APIParamType) string {
|
_typeName := func(m *APIParamType) string {
|
||||||
if m.IsArray {
|
if m.IsArray {
|
||||||
|
@ -18,9 +38,6 @@ func processTSClientOutput(f string, api *API) {
|
||||||
}
|
}
|
||||||
|
|
||||||
b := bytes.Buffer{}
|
b := bytes.Buffer{}
|
||||||
if f == "" {
|
|
||||||
f = config.Tsfname
|
|
||||||
}
|
|
||||||
|
|
||||||
b.WriteString("//#region Base\n")
|
b.WriteString("//#region Base\n")
|
||||||
b.WriteString(fmt.Sprintf(`
|
b.WriteString(fmt.Sprintf(`
|
||||||
|
@ -166,7 +183,5 @@ async function InvokeOk(path: string, method: HTMLMethod, body?: any): Promise<b
|
||||||
b.WriteString("//#endregion\n")
|
b.WriteString("//#endregion\n")
|
||||||
|
|
||||||
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
err := ioutil.WriteFile(f, b.Bytes(), 0600)
|
||||||
if err != nil {
|
return err
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func processYaml(dst string, opts interface{}) error {
|
||||||
|
bs, err := yaml.Marshal(api)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(dst, bs, 0600)
|
||||||
|
return err
|
||||||
|
}
|
|
@ -7,6 +7,11 @@ type API struct {
|
||||||
Methods map[string]*APIMethod `yaml:"methods"`
|
Methods map[string]*APIMethod `yaml:"methods"`
|
||||||
Namespace string
|
Namespace string
|
||||||
}
|
}
|
||||||
|
type APIFieldTag struct {
|
||||||
|
Key string
|
||||||
|
Name string
|
||||||
|
Opts []string
|
||||||
|
}
|
||||||
type APIField struct {
|
type APIField struct {
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
Array bool `yaml:"array"`
|
Array bool `yaml:"array"`
|
||||||
|
@ -14,6 +19,7 @@ type APIField struct {
|
||||||
Map bool `yaml:"map"`
|
Map bool `yaml:"map"`
|
||||||
Mapkey string `yaml:"mapkey"`
|
Mapkey string `yaml:"mapkey"`
|
||||||
Mapval string `yaml:"mapval"`
|
Mapval string `yaml:"mapval"`
|
||||||
|
Tags map[string]APIFieldTag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *APIField) String() string {
|
func (a *APIField) String() string {
|
||||||
|
@ -25,7 +31,7 @@ func (a *APIField) String() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type APIType struct {
|
type APIType struct {
|
||||||
Name string
|
Name string `yaml:"name"`
|
||||||
Desc string `yaml:"desc"`
|
Desc string `yaml:"desc"`
|
||||||
Fields map[string]*APIField `yaml:"fields"`
|
Fields map[string]*APIField `yaml:"fields"`
|
||||||
Col string `yaml:"col"`
|
Col string `yaml:"col"`
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "log"
|
import (
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
func Err(e error) {
|
func Err(e error) {
|
||||||
if e != nil {
|
if e != nil {
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
basepath: ""
|
||||||
|
host: ""
|
||||||
|
types:
|
||||||
|
AStr:
|
||||||
|
name: AStr
|
||||||
|
desc: ""
|
||||||
|
fields:
|
||||||
|
A:
|
||||||
|
type: string
|
||||||
|
array: false
|
||||||
|
desc: ""
|
||||||
|
map: false
|
||||||
|
mapkey: ""
|
||||||
|
mapval: ""
|
||||||
|
B:
|
||||||
|
type: string
|
||||||
|
array: true
|
||||||
|
desc: ""
|
||||||
|
map: false
|
||||||
|
mapkey: ""
|
||||||
|
mapval: ""
|
||||||
|
col: ""
|
||||||
|
methods:
|
||||||
|
SomeAPI:
|
||||||
|
desc: SomeAPI
|
||||||
|
verb: PUT
|
||||||
|
path: /someapi
|
||||||
|
perm: ASD
|
||||||
|
reqtype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: false
|
||||||
|
restype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: true
|
||||||
|
raw: false
|
||||||
|
SomeAPI2:
|
||||||
|
desc: SomeAPI2
|
||||||
|
verb: POST
|
||||||
|
path: /someapi
|
||||||
|
perm: ""
|
||||||
|
reqtype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: false
|
||||||
|
restype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: true
|
||||||
|
raw: false
|
||||||
|
SomeAPI3:
|
||||||
|
desc: SomeAPI3
|
||||||
|
verb: POST
|
||||||
|
path: /someapi3
|
||||||
|
perm: ""
|
||||||
|
reqtype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: false
|
||||||
|
restype:
|
||||||
|
typename: AStr
|
||||||
|
ispointer: true
|
||||||
|
isarray: true
|
||||||
|
raw: false
|
||||||
|
namespace: ""
|
|
@ -1,63 +0,0 @@
|
||||||
package goapi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type API struct {
|
|
||||||
Mux *http.ServeMux
|
|
||||||
Perms map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *API) GetPerm(r *http.Request) string {
|
|
||||||
return a.Perms[r.Method+"_"+strings.Split(r.RequestURI, "?")[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
func Init() API {
|
|
||||||
mux := &http.ServeMux{}
|
|
||||||
|
|
||||||
ret := API{
|
|
||||||
Mux: mux,
|
|
||||||
Perms: make(map[string]string),
|
|
||||||
}
|
|
||||||
|
|
||||||
mux.HandleFunc("/someapi", func(w http.ResponseWriter, r *http.Request) {
|
|
||||||
switch r.Method {
|
|
||||||
case "POST":
|
|
||||||
h_SomeAPI2(w, r)
|
|
||||||
default:
|
|
||||||
http.Error(w, "Method not allowed", 500)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
func h_SomeAPI2(w http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
ctx := r.Context()
|
|
||||||
ctx = context.WithValue(r.Context(), "REQ", r)
|
|
||||||
ctx = context.WithValue(ctx, "RES", w)
|
|
||||||
req := &AStr{}
|
|
||||||
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
||||||
err := json.NewDecoder(r.Body).Decode(&req)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := SomeAPI2(ctx, req)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Header().Add("Content-Type", "Application/json")
|
|
||||||
err = json.NewEncoder(w).Encode(res)
|
|
||||||
if err != nil {
|
|
||||||
http.Error(w, err.Error(), 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
package goapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type API struct {
|
||||||
|
Mux *http.ServeMux
|
||||||
|
Perms map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) GetPerm(r *http.Request) string {
|
||||||
|
return a.Perms[r.Method+"_"+strings.Split(r.RequestURI, "?")[0]]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() API {
|
||||||
|
mux := &http.ServeMux{}
|
||||||
|
|
||||||
|
ret := API{
|
||||||
|
Mux: mux,
|
||||||
|
Perms: make(map[string]string),
|
||||||
|
}
|
||||||
|
|
||||||
|
mux.HandleFunc("/someapi", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
h_SomeAPI2(w, r)
|
||||||
|
case "PUT":
|
||||||
|
h_SomeAPI(w, r)
|
||||||
|
default:
|
||||||
|
http.Error(w, "Method not allowed", 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mux.HandleFunc("/someapi3", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
switch r.Method {
|
||||||
|
case "POST":
|
||||||
|
h_SomeAPI3(w, r)
|
||||||
|
default:
|
||||||
|
http.Error(w, "Method not allowed", 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func h_SomeAPI(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(r.Context(), "REQ", r)
|
||||||
|
ctx = context.WithValue(ctx, "RES", w)
|
||||||
|
req := &Noop{}
|
||||||
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := SomeAPI(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "Application/json")
|
||||||
|
err = json.NewEncoder(w).Encode(res)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func h_SomeAPI2(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(r.Context(), "REQ", r)
|
||||||
|
ctx = context.WithValue(ctx, "RES", w)
|
||||||
|
req := &AStr2{}
|
||||||
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := SomeAPI2(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "Application/json")
|
||||||
|
err = json.NewEncoder(w).Encode(res)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func h_SomeAPI3(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
ctx := r.Context()
|
||||||
|
ctx = context.WithValue(r.Context(), "REQ", r)
|
||||||
|
ctx = context.WithValue(ctx, "RES", w)
|
||||||
|
req := &AStr{}
|
||||||
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := SomeAPI3(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Add("Content-Type", "Application/json")
|
||||||
|
err = json.NewEncoder(w).Encode(res)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,42 +2,68 @@ package goapi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
///*@API*/
|
/*@API*/
|
||||||
//type ARequestStruct struct {
|
type ARequestStruct struct {
|
||||||
// A *string `json:"a"`
|
A *string `json:"SUPERCALIFRAGILISPEALIDOUX"`
|
||||||
// B int64
|
B int64 `json:"bcd"`
|
||||||
// C time.Time
|
C time.Time
|
||||||
// D *string
|
D *string
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
///*@API*/
|
/*@API*/
|
||||||
//type AResponseStruct struct {
|
type AResponseStruct struct {
|
||||||
// A string
|
A string
|
||||||
// B int64
|
B int64
|
||||||
// C time.Time
|
C time.Time
|
||||||
// D *string
|
D *string
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
///*
|
|
||||||
//@API
|
|
||||||
//@PATH: /someapi
|
|
||||||
//*/
|
|
||||||
//func SomeAPI(ctx context.Context, a *ARequestStruct) (*AResponseStruct, error) {
|
|
||||||
// return nil, nil
|
|
||||||
//}
|
|
||||||
|
|
||||||
/*@API*/
|
/*@API*/
|
||||||
type AStr struct {
|
type AStr struct {
|
||||||
A string
|
Country string
|
||||||
|
City string
|
||||||
|
HouseNumber int64
|
||||||
|
IsCondo bool
|
||||||
|
SomeWeirdTest string `json:"SUPERCALIFRAGILISPEALIDOUX"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@API*/
|
||||||
|
type AStr2 struct {
|
||||||
|
Firstname string `json:"firstname"`
|
||||||
|
Lastname string `json:"lastname"`
|
||||||
|
Products []string
|
||||||
|
Addresses map[string]AStr
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@API*/
|
||||||
|
type Noop struct{}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@API
|
||||||
|
@PATH: /someapi
|
||||||
|
@PERM: ASD
|
||||||
|
@VERB: PUT
|
||||||
|
*/
|
||||||
|
func SomeAPI(ctx context.Context, a *Noop) ([]*AStr, error) {
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@API
|
@API
|
||||||
@PATH: /someapi
|
@PATH: /someapi
|
||||||
*/
|
*/
|
||||||
func SomeAPI2(ctx context.Context, a *AStr) ([]*AStr, error) {
|
func SomeAPI2(ctx context.Context, a *AStr2) ([]*AStr, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@API
|
||||||
|
@PATH: /someapi3
|
||||||
|
*/
|
||||||
|
func SomeAPI3(ctx context.Context, a *AStr) ([]*AStr, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@ package gocli
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Basepath string = ""
|
var Basepath string = ""
|
||||||
|
@ -33,18 +36,79 @@ func invoke(m string, path string, bodyo interface{}) (*json.Decoder, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
|
bs, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return nil, errors.New(string(bs))
|
||||||
|
}
|
||||||
|
|
||||||
ret := json.NewDecoder(res.Body)
|
ret := json.NewDecoder(res.Body)
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type AStr struct {
|
type ARequestStruct struct {
|
||||||
A string `json:"a"`
|
C time.Time `json:"c"`
|
||||||
|
D string `json:"d"`
|
||||||
|
A string `json:"a"`
|
||||||
|
B int64 `json:"b"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SomeAPI2(req *AStr) (res []*AStr, err error) {
|
type AResponseStruct struct {
|
||||||
dec, err := invoke("POST", "/someapi", req)
|
A string `json:"a"`
|
||||||
|
B int64 `json:"b"`
|
||||||
|
C time.Time `json:"c"`
|
||||||
|
D string `json:"d"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AStr struct {
|
||||||
|
A string `json:"a"`
|
||||||
|
B []string `json:"b"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AStr2 struct {
|
||||||
|
X string `json:"x"`
|
||||||
|
Y []string `json:"y"`
|
||||||
|
Z []AStr `json:"z"`
|
||||||
|
W map[string]AStr `json:"w"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SomeAPI3(req *AStr) (res []*AStr, err error) {
|
||||||
|
var dec *json.Decoder
|
||||||
|
dec, err = invoke("POST", "/someapi3", req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
ret := []*AStr{}
|
ret := []*AStr{}
|
||||||
err = dec.Decode(ret)
|
err = dec.Decode(&ret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
func SomeAPI(req *AStr) (res []*AStr, err error) {
|
||||||
|
var dec *json.Decoder
|
||||||
|
dec, err = invoke("PUT", "/someapi", req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret := []*AStr{}
|
||||||
|
err = dec.Decode(&ret)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return ret, err
|
||||||
|
}
|
||||||
|
func SomeAPI2(req *AStr) (res []*AStr, err error) {
|
||||||
|
var dec *json.Decoder
|
||||||
|
dec, err = invoke("POST", "/someapi", req)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ret := []*AStr{}
|
||||||
|
err = dec.Decode(&ret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
###
|
||||||
|
#SomeAPI
|
||||||
|
PUT https://host/basepath/someapi
|
||||||
|
Content-Type: application/json
|
||||||
|
Cookie: dc=<MYCOOKIE>
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
###
|
||||||
|
#SomeAPI2
|
||||||
|
POST https://host/basepath/someapi
|
||||||
|
Content-Type: application/json
|
||||||
|
Cookie: dc=<MYCOOKIE>
|
||||||
|
|
||||||
|
{
|
||||||
|
"addresses": {
|
||||||
|
"a": {
|
||||||
|
"SUPERCALIFRAGILISPEALIDOUX": "A STRING VALUE",
|
||||||
|
"city": "A STRING VALUE",
|
||||||
|
"country": "A STRING VALUE",
|
||||||
|
"housenumber": 123456,
|
||||||
|
"iscondo": true
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"SUPERCALIFRAGILISPEALIDOUX": "A STRING VALUE",
|
||||||
|
"city": "A STRING VALUE",
|
||||||
|
"country": "A STRING VALUE",
|
||||||
|
"housenumber": 123456,
|
||||||
|
"iscondo": true
|
||||||
|
},
|
||||||
|
"c": {
|
||||||
|
"SUPERCALIFRAGILISPEALIDOUX": "A STRING VALUE",
|
||||||
|
"city": "A STRING VALUE",
|
||||||
|
"country": "A STRING VALUE",
|
||||||
|
"housenumber": 123456,
|
||||||
|
"iscondo": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"firstname": "A STRING VALUE",
|
||||||
|
"lastname": "A STRING VALUE",
|
||||||
|
"products": [
|
||||||
|
"A STRING VALUE",
|
||||||
|
"A STRING VALUE",
|
||||||
|
"A STRING VALUE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
#SomeAPI3
|
||||||
|
POST https://host/basepath/someapi3
|
||||||
|
Content-Type: application/json
|
||||||
|
Cookie: dc=<MYCOOKIE>
|
||||||
|
|
||||||
|
{
|
||||||
|
"SUPERCALIFRAGILISPEALIDOUX": "A STRING VALUE",
|
||||||
|
"city": "A STRING VALUE",
|
||||||
|
"country": "A STRING VALUE",
|
||||||
|
"housenumber": 123456,
|
||||||
|
"iscondo": true
|
||||||
|
}
|
||||||
|
|
|
@ -83,15 +83,49 @@ async function InvokeOk(path: string, method: HTMLMethod, body?: any): Promise<b
|
||||||
//#region Types
|
//#region Types
|
||||||
export interface AStr {
|
export interface AStr {
|
||||||
a:string
|
a:string
|
||||||
|
b:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AStr2 {
|
||||||
|
z:AStr
|
||||||
|
w:{[s:string]:AStr}
|
||||||
|
x:string
|
||||||
|
y:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ARequestStruct {
|
||||||
|
b:number
|
||||||
|
c:Date
|
||||||
|
d:string
|
||||||
|
a:string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AResponseStruct {
|
||||||
|
a:string
|
||||||
|
b:number
|
||||||
|
c:Date
|
||||||
|
d:string
|
||||||
}
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Methods
|
//#region Methods
|
||||||
|
/**
|
||||||
|
SomeAPI*/
|
||||||
|
export async function SomeAPI(req:AStr):Promise<AStr[]>{
|
||||||
|
return InvokeJSON("/someapi","PUT",req)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
SomeAPI2*/
|
SomeAPI2*/
|
||||||
export async function SomeAPI2(req:AStr):Promise<AStr[]>{
|
export async function SomeAPI2(req:AStr):Promise<AStr[]>{
|
||||||
return InvokeJSON("/someapi","POST",req)
|
return InvokeJSON("/someapi","POST",req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
SomeAPI3*/
|
||||||
|
export async function SomeAPI3(req:AStr):Promise<AStr[]>{
|
||||||
|
return InvokeJSON("/someapi3","POST",req)
|
||||||
|
}
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
Loading…
Reference in New Issue