diff --git a/go.mod b/go.mod index 3a9e300..52b4626 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.digitalcircle.com.br/tools/apigen -go 1.16 +go 1.17 require ( github.com/alecthomas/kong v0.2.15 diff --git a/src/comments.go b/lib/comments.go similarity index 98% rename from src/comments.go rename to lib/comments.go index d63c344..70f85bc 100644 --- a/src/comments.go +++ b/lib/comments.go @@ -1,4 +1,4 @@ -package main +package lib import ( "go/ast" diff --git a/src/config.go b/lib/config.go similarity index 97% rename from src/config.go rename to lib/config.go index cb7b4b2..fe4ff67 100644 --- a/src/config.go +++ b/lib/config.go @@ -1,4 +1,4 @@ -package main +package lib //type Config struct { // Gofname string `yaml:"gofname"` diff --git a/src/loader.go b/lib/loader.go similarity index 99% rename from src/loader.go rename to lib/loader.go index b3dcc03..92a88c3 100644 --- a/src/loader.go +++ b/lib/loader.go @@ -1,4 +1,4 @@ -package main +package lib import ( "fmt" diff --git a/src/main.go b/lib/main.go similarity index 89% rename from src/main.go rename to lib/main.go index af4b48e..16fdb94 100644 --- a/src/main.go +++ b/lib/main.go @@ -1,4 +1,4 @@ -package main +package lib import ( "github.com/alecthomas/kong" @@ -18,9 +18,9 @@ var CLI struct { Goserver struct { Src string `arg help:"Source Dir"` } `cmd help:"Gens GO Server impl"` - Gin struct { - Src string `arg help:"Source Dir"` - } `cmd help:"Gens Gin Server impl"` + //Gin struct { + // Src string `arg help:"Source Dir"` + //} `cmd help:"Gens Gin Server impl"` Gocli struct { Src string `arg help:"Source Dir"` Dst string `arg help:"Dst file"` @@ -39,7 +39,7 @@ var CLI struct { } `cmd help:"Gens Http call impl"` } -func main() { +func Run() { var processor func() error kong.ConfigureHelp(kong.HelpOptions{ @@ -65,12 +65,12 @@ func main() { processor = func() error { return processGoServerOutput(CLI.Goserver.Src + "/apigen.go") } - case "gin ": - log.Printf("Gen Gin Server") - src = CLI.Gin.Src - processor = func() error { - return processGinServerOutput(CLI.Gin.Src + "/apigen.go") - } + //case "gin ": + // log.Printf("Gen Gin Server") + // src = CLI.Gin.Src + // processor = func() error { + // return processGinServerOutput(CLI.Gin.Src + "/apigen.go") + // } case "gocli ": log.Printf("Gen GO Client") src = CLI.Gocli.Src diff --git a/lib/processGoClientOutput.go b/lib/processGoClientOutput.go new file mode 100644 index 0000000..2c26894 --- /dev/null +++ b/lib/processGoClientOutput.go @@ -0,0 +1,141 @@ +package lib + +import ( + "bytes" + "fmt" + "os" +) + +import ( + _ "embed" +) + +func processGoClientOutput(f string) error { + + buf := &bytes.Buffer{} + W := func(s string, p ...interface{}) { + buf.WriteString(fmt.Sprintf(s, p...)) + } + WNL := func(s string, p ...interface{}) { + buf.WriteString(fmt.Sprintf(s+"\n", p...)) + } + + ResDecType := func(v *APIParamType) string { + ret := "" + if v.IsArray { + ret = ret + "[]" + } + if v.Ispointer { + ret = ret + "*" + } + ret += " " + v.Typename + return ret + } + + ResImplType := func(v *APIParamType) string { + ret := "" + if v.IsArray { + ret = ret + "[]" + } + if v.Ispointer { + ret = ret + "*" + } + ret += v.Typename + ret += " = " + if !v.IsArray || v.Ispointer { + ret = ret + "&" + } + ret += v.Typename + "{}" + return ret + } + + WNL("package %s", api.Namespace) + WNL(`import ( + "bytes" + "errors" + "io/ioutil" + "encoding/json" + "net/http" + "time" +) +var Basepath string = "" +var Host string = "" +var ExtraHeaders map[string]string = make(map[string]string) + +func invoke(m string, path string, bodyo interface{}) (*json.Decoder, error) { + b := &bytes.Buffer{} + err := json.NewEncoder(b).Encode(bodyo) + if err != nil { + return nil, err + } + + body := bytes.NewReader(b.Bytes()) + req, err := http.NewRequest(m, Host+Basepath+path, body) + if err != nil { + return nil, err + } + + req.Header.Set("Content-type", "application/json") + + for k, v := range ExtraHeaders { + req.Header.Set(k, v) + } + + cli := http.Client{} + res, err := cli.Do(req) + + if err != nil { + return nil, err + } + + defer res.Body.Close() + + if res.StatusCode >= 400 { + bs, err := ioutil.ReadAll(res.Body) + if err != nil { + return nil,err + } + return nil, errors.New(string(bs)) + } + + ret := json.NewDecoder(res.Body) + return ret, nil +} +`) + + for k, v := range api.Types { + WNL(`type %s struct {`, k) + for fn, f := range v.Fields { + W(` %s `, fn) + if f.Array { + W("[]") + } + if f.Map { + W("map[%s]%s", f.Mapkey, f.Mapval) + } else { + WNL(f.Type) + } + } + WNL(`}`) + } + + for k, v := range api.Methods { + WNL(`func %s(req %s) (res %s, err error){`, k, ResDecType(v.ReqType), ResDecType(v.ResType)) + WNL(` var dec *json.Decoder + dec, err = invoke("%s", "%s", res) + if err!=nil{ + return + } + var ret %s`, v.Verb, v.Path, ResImplType(v.ResType)) + W(` err = dec.Decode(`) + if v.ResType.IsArray || !v.ResType.Ispointer { + W("&") + } + WNL(`ret) + return ret, err +}`) + + } + + return os.WriteFile(f, buf.Bytes(), 0600) +} diff --git a/lib/processGoServerOutput.go b/lib/processGoServerOutput.go new file mode 100644 index 0000000..18ab005 --- /dev/null +++ b/lib/processGoServerOutput.go @@ -0,0 +1,123 @@ +package lib + +import ( + "bytes" + _ "embed" + "fmt" + "os" +) + +func processGoServerOutput(f string) error { + buf := &bytes.Buffer{} + W := func(s string, p ...interface{}) { + buf.WriteString(fmt.Sprintf(s, p...)) + } + WNL := func(s string, p ...interface{}) { + buf.WriteString(fmt.Sprintf(s+"\n", p...)) + } + + WNL("package %s", api.Namespace) + WNL(`import ( + "context" + "encoding/json" + "strings" + "net/http" + )`) + + for k := range api.UsedImportsFunctions { + W(`import "%s"`, k) + } + + WNL(`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]] + } +`) + + WNL(`func Init() *API{ + mux := &http.ServeMux{} + + ret := &API{ + Mux: mux, + Perms: make(map[string]string), + }`) + for _, v := range api.Methods { + if v.Perm != "" { + WNL(` ret.Perms["%s_%s"]="%s"`, v.Verb, v.Path, v.Perm) + } + + } + for _, v := range api.SortedPaths { + WNL(` mux.HandleFunc("%s",func(w http.ResponseWriter, r *http.Request) { + switch r.Method {`, v.Path) + for _, v1 := range v.SortedVerbs { + WNL(` case "%s":`, v1.Method.Verb) + if v1.Method.Raw { + WNL(` %s(w,r)`, v1.Method.Name) + } else { + WNL(` h_%s(w,r)`, v1.Method.Name) + } + + WNL(` default: + http.Error(w,"Method not allowed",500)`) + } + + WNL(` }`) + + WNL(` }) + return ret + }`) + + } + + for _, v := range api.Methods { + WNL(`func h_%s(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + ctx = context.WithValue(r.Context(), "REQ", r) + ctx = context.WithValue(ctx, "RES", w)`, v.Name) + + W(" var req ") + + if v.ReqType.IsArray { + W("[]") + } + if v.ReqType.Ispointer { + W("*") + } + WNL(v.ReqType.Typename) + + WNL(` if r.Method!=http.MethodGet && r.Method!=http.MethodHead {`) + + if v.ReqType.Ispointer || v.ReqType.IsArray { + WNL(" err := json.NewDecoder(r.Body).Decode(req)") + } else { + WNL(" err := json.NewDecoder(r.Body).Decode(&req)") + } + + WNL(` if err != nil { + http.Error(w, err.Error(), 500) + return + } + }`) + + WNL(` res, err := %s(ctx,req)`, v.Name) + WNL(` 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 + } +}`) + + } + + return os.WriteFile(f, buf.Bytes(), 0600) +} diff --git a/src/processHTTPCallOutput.go b/lib/processHTTPCallOutput.go similarity index 99% rename from src/processHTTPCallOutput.go rename to lib/processHTTPCallOutput.go index 618a4f2..7d18330 100644 --- a/src/processHTTPCallOutput.go +++ b/lib/processHTTPCallOutput.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bytes" diff --git a/src/processPyClientOutput.go b/lib/processPyClientOutput.go similarity index 99% rename from src/processPyClientOutput.go rename to lib/processPyClientOutput.go index f4e81a2..e144f6a 100644 --- a/src/processPyClientOutput.go +++ b/lib/processPyClientOutput.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bytes" diff --git a/src/processTSClientOutput.go b/lib/processTSClientOutput.go similarity index 99% rename from src/processTSClientOutput.go rename to lib/processTSClientOutput.go index 263c603..9c5e298 100644 --- a/src/processTSClientOutput.go +++ b/lib/processTSClientOutput.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bytes" diff --git a/src/processYaml.go b/lib/processYaml.go similarity index 94% rename from src/processYaml.go rename to lib/processYaml.go index 098cf5f..2bf64fc 100644 --- a/src/processYaml.go +++ b/lib/processYaml.go @@ -1,4 +1,4 @@ -package main +package lib import ( "gopkg.in/yaml.v2" diff --git a/src/types.go b/lib/types.go similarity index 98% rename from src/types.go rename to lib/types.go index 0da9f53..a62dfd8 100644 --- a/src/types.go +++ b/lib/types.go @@ -1,4 +1,4 @@ -package main +package lib type API struct { BasePath string `yaml:"basepath,omitempty"` @@ -66,6 +66,7 @@ type APIMethod struct { Verb string `yaml:"verb"` Path string `yaml:"path"` Perm string `yaml:perm` + Raw bool `yaml:"raw"` ReqType *APIParamType ResType *APIParamType } diff --git a/src/util.go b/lib/util.go similarity index 93% rename from src/util.go rename to lib/util.go index fa8cc20..79346b0 100644 --- a/src/util.go +++ b/lib/util.go @@ -1,4 +1,4 @@ -package main +package lib import ( "log" @@ -17,4 +17,3 @@ func Debug(s string, p ...interface{}) { func Log(s string, p ...interface{}) { log.Printf("LOG: "+s, p...) } - diff --git a/main.go b/main.go new file mode 100644 index 0000000..27a215b --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "go.digitalcircle.com.br/tools/apigen/lib" + +func main() { + lib.Run() +} diff --git a/src/processGinServerOutput.go b/src/processGinServerOutput.go deleted file mode 100644 index 3ba713d..0000000 --- a/src/processGinServerOutput.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "bytes" - _ "embed" - "os" - "text/template" -) - -//go:embed templates/goserver-gin.gotmpl -var ginServerTemplate string - -func processGinServerOutput(f string) error { - tmpl, err := template.New("gin").Parse(ginServerTemplate) - if err != nil { - return err - } - buf := &bytes.Buffer{} - err = tmpl.Execute(buf, api) - if err != nil { - return err - } - return os.WriteFile(f, buf.Bytes(), 0600) -} diff --git a/src/processGoClientOutput.go b/src/processGoClientOutput.go deleted file mode 100644 index 1c116c7..0000000 --- a/src/processGoClientOutput.go +++ /dev/null @@ -1,27 +0,0 @@ -package main - -import ( - "bytes" -) - -import ( - _ "embed" - "os" - "text/template" -) - -//go:embed templates/goclient.gotmpl -var goCliTemplate string - -func processGoClientOutput(f string) error { - tmpl, err := template.New("gin").Parse(goCliTemplate) - if err != nil { - return err - } - buf := &bytes.Buffer{} - err = tmpl.Execute(buf, api) - if err != nil { - return err - } - return os.WriteFile(f, buf.Bytes(), 0600) -} diff --git a/src/processGoServerOutput.go b/src/processGoServerOutput.go deleted file mode 100644 index 5033a89..0000000 --- a/src/processGoServerOutput.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "bytes" - _ "embed" - "os" - "text/template" -) - -//go:embed templates/goserver.gotmpl -var goServerTemplate string - -func processGoServerOutput(f string) error { - tmpl, err := template.New("go").Parse(goServerTemplate) - if err != nil { - return err - } - buf := &bytes.Buffer{} - err = tmpl.Execute(buf, api) - if err != nil { - return err - } - return os.WriteFile(f, buf.Bytes(), 0600) -} diff --git a/src/templates/goclient.gotmpl b/src/templates/goclient.gotmpl deleted file mode 100644 index 1c983ea..0000000 --- a/src/templates/goclient.gotmpl +++ /dev/null @@ -1,74 +0,0 @@ -package {{.Namespace}} - -import ( -"bytes" -"errors" -"io/ioutil" -"encoding/json" -"net/http" -"time" -) - -var Basepath string = "" -var Host string = "" -var ExtraHeaders map[string]string = make(map[string]string) - -func invoke(m string, path string, bodyo interface{}) (*json.Decoder, error) { -b := &bytes.Buffer{} -err := json.NewEncoder(b).Encode(bodyo) -if err != nil { -return nil, err -} -body := bytes.NewReader(b.Bytes()) -req, err := http.NewRequest(m, Host+Basepath+path, body) -if err != nil { -return nil, err -} - -req.Header.Set("Content-type", "application/json") - -for k, v := range ExtraHeaders { -req.Header.Set(k, v) -} - -cli := http.Client{} -res, err := cli.Do(req) - -if err != nil { -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) -return ret, nil -} - -{{range $typename,$type := .Types}} - type {{$typename}} struct{ - {{range $fieldname, $field:= $type.Fields}} - {{$fieldname}} {{ if $field.Array}}[]{{end}}{{if $field.Map}}map[{{$field.Mapkey}}]{{$field.Mapval}}{{else}}{{$field.Type}}{{end}} - {{end}} - } -{{end}} - -{{range $methodname,$method :=.Methods}} -func {{$methodname}}(req {{if $method.ReqType.IsArray}}[]{{end}}{{if $method.ReqType.Ispointer}}*{{end}}{{$method.ReqType.Typename}}) (res {{if $method.ResType.IsArray}}[]{{end}}{{if $method.ResType.Ispointer}}*{{end}}{{$method.ResType.Typename}}, err error){ - var dec *json.Decoder - dec, err = invoke("{{$method.Verb}}", "{{$method.Path}}", req) - if err!=nil{ - return - } - var ret {{if $method.ResType.IsArray}}[]{{end}}{{if $method.ResType.Ispointer}}*{{end}}{{$method.ResType.Typename}} {{if $method.ResType.Ispointer}} {{if ne $method.ResType.IsArray true}}=&{{$method.ResType.Typename}}{}{{end}}{{end}} - err = dec.Decode({{if $method.ResType.IsArray}}&{{end}} {{if ne $method.ResType.Ispointer true}}&{{end}}ret) - return ret, err -} -{{end}} - diff --git a/src/templates/goserver-gin.gotmpl b/src/templates/goserver-gin.gotmpl deleted file mode 100644 index ec0786d..0000000 --- a/src/templates/goserver-gin.gotmpl +++ /dev/null @@ -1,55 +0,0 @@ -package {{.Namespace}} - -import ( - "github.com/gin-gonic/gin" -) - -{{range $impalias, $impname := .UsedImportsFunctions}} - {{if and ( ne $impname "context") (ne $impname "json") (ne $impname "strings") (ne $impname "net/http") -}} - import "{{.}}" - {{- end}} -{{end}} -var perms map[string]string -func init(){ - perms=make(map[string]string) -{{range .Methods -}} - {{if .Perm}} - perms["{{.Verb}}_{{.Path}}"]="{{.Perm}}" - {{- end}} -{{end}} - -} - -func GetPerm(c *gin.Context) string { - perm, ok := perms[c.Request.Method+"_"+c.Request.URL.Path] - if !ok { - return "" - } - return perm -} - -func Build(r *gin.Engine) *gin.Engine { - -{{range $pathname,$path:= .SortedPaths -}} - {{range $verbname,$verb :=.SortedVerbs -}} - r.{{$verb.Verb}}("{{$path.Path}}", func(c *gin.Context) { - var req {{if $verb.Method.ReqType.IsArray -}}[]{{ end -}} - {{- if $verb.Method.ReqType.Ispointer}} *{{ end -}} - {{- $verb.Method.ReqType.Typename}} - {{- if or $verb.Method.ReqType.IsArray $verb.Method.ReqType.Ispointer}} - {{- if $verb.Method.ReqType.Ispointer}} = &{{- $verb.Method.ReqType.Typename}}{}{{ end}} - c.BindJSON(req) - {{else}} - c.BindJSON(&req) - {{end -}} - res,err:= {{$verb.Method.Name}}(c.Request.Context(),req) - if err!=nil{ - c.Error(err) - } - c.JSON(200,res) - }) - {{end}} -{{end -}} - -return r -} diff --git a/src/templates/goserver.go.tmpl b/src/templates/goserver.go.tmpl deleted file mode 100644 index 12ea6df..0000000 --- a/src/templates/goserver.go.tmpl +++ /dev/null @@ -1,38 +0,0 @@ -package {{.Package}} - -import ( - "context" - "encoding/json" - "strings" - "net/http" -) - -{{range .Imports}} - import {{.}} -{{end}} - - -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), - } - - {{range .Methods}} - {{if .Perm}} - ret.Perms["{{.Verb}}_{{.Path}}"]="{{.Perm}}" - {{end}} - {{end}} -} \ No newline at end of file diff --git a/src/templates/goserver.gotmpl b/src/templates/goserver.gotmpl deleted file mode 100644 index 98a1d1a..0000000 --- a/src/templates/goserver.gotmpl +++ /dev/null @@ -1,95 +0,0 @@ -package {{.Namespace}} - -import ( - "context" - "encoding/json" - "strings" - "net/http" -) - -{{range $impalias, $impname := .UsedImportsFunctions}} -{{if and ( ne $impname "context") (ne $impname "json") (ne $impname "strings") (ne $impname "net/http") -}} -import "{{.}}" -{{- end}} -{{end}} - - -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), - } - - {{range .Methods -}} - {{if .Perm -}} - ret.Perms["{{.Verb}}_{{.Path}}"]="{{.Perm}}" - {{- end}} - {{end}} - - {{range .SortedPaths}} - mux.HandleFunc("{{.Path}}",func(w http.ResponseWriter, r *http.Request) { - switch r.Method{ - {{range .SortedVerbs -}} - case "{{.Verb}}": - {{if .Method.Raw -}} - {{.Method.Name}}(w , r) - {{else -}} - h_{{.Method.Name}}(w , r) - {{end -}} - {{end -}} - default: - http.Error(w,"Method not allowed",500) - } - - }) - {{end}} - - return &ret -} - -{{range $MethodName, $Method := .Methods}} - - -func h_{{$MethodName}}(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - ctx = context.WithValue(r.Context(), "REQ", r) - ctx = context.WithValue(ctx, "RES", w) - var req {{if $Method.ReqType.IsArray}}[]{{end}}{{if $Method.ReqType.Ispointer}}*{{end}}{{$Method.ReqType.Typename}} - if r.Method!=http.MethodGet && r.Method!=http.MethodHead { - {{if or $Method.ReqType.IsArray $Method.ReqType.Ispointer}} - err := json.NewDecoder(r.Body).Decode(req) - {{else}} - err := json.NewDecoder(r.Body).Decode(&req) - {{end}} - - if err != nil { - http.Error(w, err.Error(), 500) - return - } - } - - res, err := {{$MethodName}}(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 - } -} -{{end}}