From b7124b259fc978732f981a89aeaa2b0480042469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paulo=20Sim=C3=A3o?= Date: Fri, 2 Apr 2021 11:40:28 -0300 Subject: [PATCH] Templates --- LICENSE | 21 ++ README.md | 2 + go.mod | 3 +- go.sum | 132 +++------ src/loader.go | 64 +++- src/main.go | 9 + src/processGinServerOutput.go | 24 ++ src/processGoClientOutput.go | 110 +------ src/processGoServerOutput.go | 161 +--------- src/templates/goclient.gotmpl | 74 +++++ src/templates/goserver-gin.gotmpl | 54 ++++ src/templates/goserver.go.tmpl | 38 +++ src/templates/goserver.gotmpl | 95 ++++++ src/types.go | 34 ++- src/util.go | 1 + test/api.yaml | 471 ++++++++++++++++++++++++++++++ test/goapi/apigen.go | 105 ++++--- test/goapi/test.go | 28 +- test/gocli/cli.go | 117 ++++++++ test/main.go | 8 +- 20 files changed, 1121 insertions(+), 430 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/processGinServerOutput.go create mode 100644 src/templates/goclient.gotmpl create mode 100644 src/templates/goserver-gin.gotmpl create mode 100644 src/templates/goserver.go.tmpl create mode 100644 src/templates/goserver.gotmpl create mode 100644 test/api.yaml create mode 100644 test/gocli/cli.go diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b080007 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Digital Circle + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..687313d --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# apigen +API Generator diff --git a/go.mod b/go.mod index 99b5217..3a9e300 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,7 @@ go 1.16 require ( github.com/alecthomas/kong v0.2.15 github.com/fatih/structtag v1.2.0 + github.com/gin-gonic/gin v1.6.3 github.com/pkg/errors v0.9.1 - go.digitalcircle.com.br/dc/golib v0.0.0-20210321170103-c3e68008b19e // indirect - go.digitalcircle.com.br/golib/base v0.0.0-20210124165830-341c1d300435 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 885ba81..8ee6cef 100644 --- a/go.sum +++ b/go.sum @@ -1,120 +1,56 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/alecthomas/kong v0.2.15 h1:HP3K1XuFn0wGSWFGVW67V+65tXw/Ht8FDYiLNAuX2Ug= github.com/alecthomas/kong v0.2.15/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE= -github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 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/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -go.digitalcircle.com.br/dc/golib v0.0.0-20210321170103-c3e68008b19e h1:AJU6WBeMg8y0ijBabhpjS3rhlIpdNlQNv8sTLmVmmns= -go.digitalcircle.com.br/dc/golib v0.0.0-20210321170103-c3e68008b19e/go.mod h1:0DBjz1XuKGTxXge679K5ZYzkXrjbP6ImWm4kU/BlLUs= -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= -go.mongodb.org/mongo-driver v1.5.0/go.mod h1:boiGPFqyBs5R0R5qf2ErokGRekMfwn+MqKaUyHs7wy0= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/loader.go b/src/loader.go index c5a7302..55c9286 100644 --- a/src/loader.go +++ b/src/loader.go @@ -8,6 +8,7 @@ import ( "go/token" "log" "net/http" + "sort" "strings" ) @@ -48,7 +49,7 @@ func addStruct(a *ast.GenDecl) { tp.Fields[v.Names[0].Name].Type = "interface{}" tp.Fields[v.Names[0].Name].Array = true case *ast.SelectorExpr: - api.UsedImportsTypes[z.X.(*ast.Ident).Name] = true + api.UsedImportsTypes[z.X.(*ast.Ident).Name] = api.Imports[z.X.(*ast.Ident).Name] tp.Fields[v.Names[0].Name].Type = z.X.(*ast.Ident).Name + "." + z.Sel.Name tp.Fields[v.Names[0].Name].Array = true } @@ -60,7 +61,7 @@ func addStruct(a *ast.GenDecl) { case *ast.SelectorExpr: switch z := y.X.(type) { case *ast.Ident: - api.UsedImportsTypes[z.Name] = true + api.UsedImportsTypes[z.Name] = api.Imports[z.Name] tp.Fields[v.Names[0].Name].Type = z.Name + "." + y.Sel.Name } } @@ -72,7 +73,7 @@ func addStruct(a *ast.GenDecl) { switch z := x.X.(type) { case *ast.Ident: - api.UsedImportsTypes[z.Name] = true + api.UsedImportsTypes[z.Name] = api.Imports[z.Name] tp.Fields[v.Names[0].Name].Type = z.Name + "." + x.Sel.Name } @@ -135,7 +136,7 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: reqType.Typename = y.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name] reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name } case *ast.ArrayType: @@ -144,7 +145,7 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: reqType.Typename = y.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name] reqType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name case *ast.StarExpr: reqType.Ispointer = true @@ -152,14 +153,14 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: reqType.Typename = z.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = api.Imports[z.X.(*ast.Ident).Name] reqType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name } } case *ast.Ident: reqType.Typename = x.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = api.Imports[x.X.(*ast.Ident).Name] reqType.Typename = x.X.(*ast.Ident).Name + "." + x.Sel.Name } } @@ -173,7 +174,7 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: resType.Typename = y.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name] resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name } case *ast.ArrayType: @@ -182,7 +183,7 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: resType.Typename = y.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[y.X.(*ast.Ident).Name] = api.Imports[y.X.(*ast.Ident).Name] resType.Typename = y.X.(*ast.Ident).Name + "." + y.Sel.Name case *ast.StarExpr: resType.Ispointer = true @@ -190,14 +191,14 @@ func addFunction(a *ast.FuncDecl) { case *ast.Ident: resType.Typename = z.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[z.X.(*ast.Ident).Name] = api.Imports[z.X.(*ast.Ident).Name] resType.Typename = z.X.(*ast.Ident).Name + "." + z.Sel.Name } } case *ast.Ident: resType.Typename = x.Name case *ast.SelectorExpr: - api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = true + api.UsedImportsFunctions[x.X.(*ast.Ident).Name] = api.Imports[x.X.(*ast.Ident).Name] resType.Typename = x.X.(*ast.Ident).Name + "." + x.Sel.Name } @@ -212,13 +213,13 @@ func addFunction(a *ast.FuncDecl) { verb = http.MethodPost } fn := APIMethod{ + Name: a.Name.Name, Desc: a.Name.Name, Verb: verb, Path: md["PATH"], Perm: md["PERM"], ReqType: reqType, ResType: resType, - Raw: md["RAW"] == "true", } if fn.Path == "" { @@ -235,8 +236,10 @@ func load(src string) error { api.Types = (make(map[string]*APIType)) api.Methods = (make(map[string]*APIMethod)) api.Imports = make(map[string]string) - api.UsedImportsTypes = make(map[string]bool) - api.UsedImportsFunctions = make(map[string]bool) + api.UsedImportsTypes = make(map[string]string) + api.UsedImportsFunctions = make(map[string]string) + api.Paths = make(map[string]*APIPath) + api.SortedPaths = make([]*APIPath, 0) fset := token.NewFileSet() // positions are relative to fset @@ -297,6 +300,19 @@ func load(src string) error { } for k, v := range api.Methods { + path, ok := api.Paths[v.Path] + if !ok { + path = &APIPath{ + Path: v.Path, + MapVerbs: make(map[string]*APIVerb), + SortedVerbs: make([]*APIVerb, 0), + } + api.Paths[v.Path] = path + } + path.MapVerbs[v.Verb] = &APIVerb{ + Verb: v.Verb, + Method: v, + } pathmap, ok := httpMapper[v.Path] if !ok { httpMapper[v.Path] = make(map[string]string) @@ -305,5 +321,25 @@ func load(src string) error { pathmap[v.Verb] = k } + pathNames := make([]string, 0) + for k, v := range api.Paths { + verbs := make([]string, 0) + for k, _ := range v.MapVerbs { + verbs = append(verbs, k) + } + sort.Strings(verbs) + + for _, sv := range verbs { + v.SortedVerbs = append(v.SortedVerbs, v.MapVerbs[sv]) + } + pathNames = append(pathNames, k) + } + sort.Strings(pathNames) + for _, p := range pathNames { + api.SortedPaths = append(api.SortedPaths, api.Paths[p]) + } + + api.Namespace = packageName + return nil } diff --git a/src/main.go b/src/main.go index 135ecfc..af4b48e 100644 --- a/src/main.go +++ b/src/main.go @@ -18,6 +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"` Gocli struct { Src string `arg help:"Source Dir"` Dst string `arg help:"Dst file"` @@ -62,6 +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 "gocli ": log.Printf("Gen GO Client") src = CLI.Gocli.Src diff --git a/src/processGinServerOutput.go b/src/processGinServerOutput.go new file mode 100644 index 0000000..3ba713d --- /dev/null +++ b/src/processGinServerOutput.go @@ -0,0 +1,24 @@ +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 index 2cd3e95..1c116c7 100644 --- a/src/processGoClientOutput.go +++ b/src/processGoClientOutput.go @@ -2,112 +2,26 @@ package main import ( "bytes" - "fmt" - "io/ioutil" - "strings" ) -func processGoClientOutput(f string) error { - b := bytes.Buffer{} - - fparts := strings.Split(f, "/") - pkg := fparts[len(fparts)-2] - - b.WriteString(fmt.Sprintf(`package %s - import ( - "bytes" - "errors" - "io/ioutil" - "encoding/json" - "net/http" - "time" + _ "embed" + "os" + "text/template" ) -var Basepath string = "" -var Host string = "" -var ExtraHeaders map[string]string = make(map[string]string) +//go:embed templates/goclient.gotmpl +var goCliTemplate string -func invoke(m string, path string, bodyo interface{}) (*json.Decoder, error) { - - b := &bytes.Buffer{} - err := json.NewEncoder(b).Encode(bodyo) +func processGoClientOutput(f string) error { + tmpl, err := template.New("gin").Parse(goCliTemplate) if err != nil { - return nil, err + return err } - body := bytes.NewReader(b.Bytes()) - - req, err := http.NewRequest(m, Host+Basepath+path, body) + buf := &bytes.Buffer{} + err = tmpl.Execute(buf, api) if err != nil { - return nil, err + return 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 -} - -`, pkg)) - - for k, v := range api.Types { - - b.WriteString(fmt.Sprintf("type %s struct {\n", k)) - - for kf, f := range v.Fields { - - ftype := f.Type - - if f.Array { - ftype = "[]" + ftype - } else if f.Map { - fm := f.Mapkey - fv := f.Mapval - ftype = "map[" + fm + "]" + fv - } - - b.WriteString(fmt.Sprintf("\t%s %s `json:\"%s\"` \n", strings.ToUpper(kf[:1])+strings.ToLower(kf[1:]), ftype, strings.ToLower(kf))) - } - - b.WriteString(fmt.Sprintf("}\n\n")) - } - - for k, m := range api.Methods { - - b.WriteString(fmt.Sprintf("func %s(req %s) (res %s, err error){\n", k, APIParamTypeDecToString(m.ReqType), APIParamTypeDecToString(m.ResType))) - b.WriteString(fmt.Sprintf(`var dec *json.Decoder - dec, err = invoke("%s", "%s", req) - if err!=nil{ - return - } - ret := %s{} - err = dec.Decode(%sret) - if err != nil{ - return nil,err - } - return ret, err -}`, m.Verb, m.Path, APIParamTypeToString(m.ResType), APIParamTypeUseRef(m.ResType))) - b.WriteString("\n") - //} - - } - - err := ioutil.WriteFile(f, b.Bytes(), 0600) - return err + return os.WriteFile(f, buf.Bytes(), 0600) } diff --git a/src/processGoServerOutput.go b/src/processGoServerOutput.go index 40bf630..5033a89 100644 --- a/src/processGoServerOutput.go +++ b/src/processGoServerOutput.go @@ -2,160 +2,23 @@ package main import ( "bytes" - "fmt" - dc "go.digitalcircle.com.br/golib/base" - "io/ioutil" + _ "embed" "os" - "os/exec" - "sort" - "strings" + "text/template" ) +//go:embed templates/goserver.gotmpl +var goServerTemplate string + 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{} - - os.Remove(f) - b.WriteString(fmt.Sprintf(`package %s - -import ( - "context" - "encoding/json" - "strings" - "net/http" -) -`, packageName)) - - for v, _ := range api.UsedImportsFunctions { - parts := strings.Split(api.Imports[v], "/") - lpart := parts[len(parts)-1] - if v != lpart { - b.WriteString(fmt.Sprintf("import %s \"%s\"\n", v, api.Imports[v])) - } else { - b.WriteString(fmt.Sprintf("import \"%s\"\n", api.Imports[v])) - } - } - - b.WriteString(` - -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), - } -`) - for _, k := range strMKeys { - m := api.Methods[k] - if m.Perm != "" { - b.WriteString(fmt.Sprintf(` -ret.Perms["%s_%s"]="%s" -`, m.Verb, m.Path, m.Perm)) - } - } - - b.WriteString("\n\n") - - sortedMapper := make([]string, 0) - for k, _ := range httpMapper { - 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") - - 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)) - if api.Methods[id].Raw { - b.WriteString(fmt.Sprintf(" %s(w , r)\n", id)) - } else { - b.WriteString(fmt.Sprintf(" h_%s(w , r)\n", id)) - } - } - b.WriteString(" default:") - b.WriteString(" http.Error(w,\"Method not allowed\",500)") - - b.WriteString(" }\n") - b.WriteString("})\n") - } - b.WriteString("return ret\n }\n") - - for k, m := range api.Methods { - if !m.Raw { - b.WriteString(fmt.Sprintf("\n func h_%s(w http.ResponseWriter, r *http.Request) {\n", k)) - - b.WriteString(fmt.Sprintf( - ` - ctx := r.Context() - ctx = context.WithValue(r.Context(), "REQ", r) - ctx = context.WithValue(ctx, "RES", w) - var req %s - if r.Method!=http.MethodGet && r.Method!=http.MethodHead { - err := json.NewDecoder(r.Body).Decode(%sreq) - if err != nil { - http.Error(w, err.Error(), 500) - return - } - } - - res, err := %s(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 - } -} - -`, APIParamTypeToString(m.ReqType), APIParamTypeUseRef(m.ReqType), k)) - } - } - - err := ioutil.WriteFile(f, b.Bytes(), 0600) + tmpl, err := template.New("go").Parse(goServerTemplate) if err != nil { return err } - cmd := exec.Command("/bin/sh", "-c", "go fmt "+f) - bs, err := cmd.Output() - //dc.Err(err) - dc.Log(string(bs)) - 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 new file mode 100644 index 0000000..cbd3b62 --- /dev/null +++ b/src/templates/goclient.gotmpl @@ -0,0 +1,74 @@ +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}} + err = dec.Decode(ret) + return ret, err +} +{{end}} + diff --git a/src/templates/goserver-gin.gotmpl b/src/templates/goserver-gin.gotmpl new file mode 100644 index 0000000..f200d8c --- /dev/null +++ b/src/templates/goserver-gin.gotmpl @@ -0,0 +1,54 @@ +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}} + 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 new file mode 100644 index 0000000..12ea6df --- /dev/null +++ b/src/templates/goserver.go.tmpl @@ -0,0 +1,38 @@ +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 new file mode 100644 index 0000000..98a1d1a --- /dev/null +++ b/src/templates/goserver.gotmpl @@ -0,0 +1,95 @@ +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}} diff --git a/src/types.go b/src/types.go index a580bee..0da9f53 100644 --- a/src/types.go +++ b/src/types.go @@ -7,13 +7,26 @@ type API struct { Methods map[string]*APIMethod `yaml:"methods,omitempty"` Namespace string `yaml:"namespace"` Imports map[string]string `yaml:"imports"` - UsedImportsTypes map[string]bool `yaml:"used_imports_types"` - UsedImportsFunctions map[string]bool `yaml:"used_imports_functions"` + UsedImportsTypes map[string]string `yaml:"used_imports_types"` + UsedImportsFunctions map[string]string `yaml:"used_imports_functions"` + SortedPaths []*APIPath `yaml:"sorted_paths"` + Paths map[string]*APIPath `yaml:"paths"` +} + +type APIPath struct { + Path string `yaml:"path"` + MapVerbs map[string]*APIVerb `yaml:"map_verbs"` + SortedVerbs []*APIVerb `yaml:"sorted_verbs"` +} + +type APIVerb struct { + Verb string `yaml:"verb"` + Method *APIMethod `yaml:"method"` } type APIFieldTag struct { - Key string - Name string - Opts []string + Key string `yaml:"key"` + Name string `yaml:"name"` + Opts []string `yaml:"opts"` } type APIField struct { Type string `yaml:"type,omitempty"` @@ -34,10 +47,11 @@ func (a *APIField) String() string { } type APIType struct { - Name string `yaml:"name,omitempty"` - Desc string `yaml:"desc,omitempty"` - Fields map[string]*APIField `yaml:"fields,omitempty"` - Col string `yaml:"col,omitempty"` + Name string `yaml:"name,omitempty"` + Desc string `yaml:"desc,omitempty"` + Fields map[string]*APIField `yaml:"fields,omitempty"` + Col string `yaml:"col,omitempty"` + TypeDef string `yaml:"-"` } type APIParamType struct { @@ -47,13 +61,13 @@ type APIParamType struct { } type APIMethod struct { + Name string `yaml:"name"` Desc string `yaml:"desc"` Verb string `yaml:"verb"` Path string `yaml:"path"` Perm string `yaml:perm` ReqType *APIParamType ResType *APIParamType - Raw bool `yaml:"raw"` } func APIParamTypeToString(t *APIParamType) string { diff --git a/src/util.go b/src/util.go index 0e89091..fa8cc20 100644 --- a/src/util.go +++ b/src/util.go @@ -17,3 +17,4 @@ func Debug(s string, p ...interface{}) { func Log(s string, p ...interface{}) { log.Printf("LOG: "+s, p...) } + diff --git a/test/api.yaml b/test/api.yaml new file mode 100644 index 0000000..9bca7c3 --- /dev/null +++ b/test/api.yaml @@ -0,0 +1,471 @@ +types: + AStr: + name: AStr + fields: + Arrofpstr: + type: string + array: true + tags: + json: + key: json + name: arrofpstr + opts: + - omitempty + City: + type: string + Country: + type: string + HouseNumber: + type: int64 + IsCondo: + type: bool + Recursive: + map: true + mapkey: string + mapval: AStr + Some: + type: crypto.Decrypter + SomeWeirdTest: + type: string + tags: + json: + key: json + name: SUPERCALIFRAGILISPEALIDOUX + opts: [] + When: + type: time.Time +methods: + SomeAPI: + name: SomeAPI + desc: SomeAPI + verb: POST + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + SomeAPI2: + name: SomeAPI2 + desc: SomeAPI2 + verb: DELETE + path: /someapi + perm: ASD + reqtype: + typename: crypto.Hash + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: true + raw: false + SomeAPI3: + name: SomeAPI3 + desc: SomeAPI3 + verb: GET + path: /raw + perm: ASD + reqtype: + typename: "" + ispointer: true + isarray: false + restype: + typename: "" + ispointer: false + isarray: true + raw: true + SomeGET: + name: SomeGET + desc: SomeGET + verb: GET + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + SomePUT: + name: SomePUT + desc: SomePUT + verb: PUT + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false +namespace: goapi +imports: + context: context + crypto: crypto + gin: github.com/gin-gonic/gin + http: net/http + time: time +used_imports_types: + crypto: crypto + time: time +used_imports_functions: + crypto: crypto + http: net/http +sorted_paths: +- path: /raw + map_verbs: + GET: + verb: GET + method: + name: SomeAPI3 + desc: SomeAPI3 + verb: GET + path: /raw + perm: ASD + reqtype: + typename: "" + ispointer: true + isarray: false + restype: + typename: "" + ispointer: false + isarray: true + raw: true + sorted_verbs: + - verb: GET + method: + name: SomeAPI3 + desc: SomeAPI3 + verb: GET + path: /raw + perm: ASD + reqtype: + typename: "" + ispointer: true + isarray: false + restype: + typename: "" + ispointer: false + isarray: true + raw: true +- path: /someapi + map_verbs: + DELETE: + verb: DELETE + method: + name: SomeAPI2 + desc: SomeAPI2 + verb: DELETE + path: /someapi + perm: ASD + reqtype: + typename: crypto.Hash + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: true + raw: false + GET: + verb: GET + method: + name: SomeGET + desc: SomeGET + verb: GET + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + POST: + verb: POST + method: + name: SomeAPI + desc: SomeAPI + verb: POST + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + PUT: + verb: PUT + method: + name: SomePUT + desc: SomePUT + verb: PUT + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + sorted_verbs: + - verb: DELETE + method: + name: SomeAPI2 + desc: SomeAPI2 + verb: DELETE + path: /someapi + perm: ASD + reqtype: + typename: crypto.Hash + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: true + raw: false + - verb: GET + method: + name: SomeGET + desc: SomeGET + verb: GET + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + - verb: POST + method: + name: SomeAPI + desc: SomeAPI + verb: POST + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + - verb: PUT + method: + name: SomePUT + desc: SomePUT + verb: PUT + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false +paths: + /raw: + path: /raw + map_verbs: + GET: + verb: GET + method: + name: SomeAPI3 + desc: SomeAPI3 + verb: GET + path: /raw + perm: ASD + reqtype: + typename: "" + ispointer: true + isarray: false + restype: + typename: "" + ispointer: false + isarray: true + raw: true + sorted_verbs: + - verb: GET + method: + name: SomeAPI3 + desc: SomeAPI3 + verb: GET + path: /raw + perm: ASD + reqtype: + typename: "" + ispointer: true + isarray: false + restype: + typename: "" + ispointer: false + isarray: true + raw: true + /someapi: + path: /someapi + map_verbs: + DELETE: + verb: DELETE + method: + name: SomeAPI2 + desc: SomeAPI2 + verb: DELETE + path: /someapi + perm: ASD + reqtype: + typename: crypto.Hash + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: true + raw: false + GET: + verb: GET + method: + name: SomeGET + desc: SomeGET + verb: GET + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + POST: + verb: POST + method: + name: SomeAPI + desc: SomeAPI + verb: POST + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + PUT: + verb: PUT + method: + name: SomePUT + desc: SomePUT + verb: PUT + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + sorted_verbs: + - verb: DELETE + method: + name: SomeAPI2 + desc: SomeAPI2 + verb: DELETE + path: /someapi + perm: ASD + reqtype: + typename: crypto.Hash + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: true + raw: false + - verb: GET + method: + name: SomeGET + desc: SomeGET + verb: GET + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + - verb: POST + method: + name: SomeAPI + desc: SomeAPI + verb: POST + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false + - verb: PUT + method: + name: SomePUT + desc: SomePUT + verb: PUT + path: /someapi + perm: ASD + reqtype: + typename: string + ispointer: false + isarray: false + restype: + typename: string + ispointer: false + isarray: false + raw: false diff --git a/test/goapi/apigen.go b/test/goapi/apigen.go index d691fc7..812f154 100644 --- a/test/goapi/apigen.go +++ b/test/goapi/apigen.go @@ -1,63 +1,72 @@ package goapi import ( - "context" - "encoding/json" - "net/http" - "strings" + "github.com/gin-gonic/gin" ) -type API struct { - Mux *http.ServeMux - Perms map[string]string +import "crypto" + +var perms map[string]string + +func init() { + perms = make(map[string]string) + + perms["POST_/someapi"] = "ASD" + + perms["DELETE_/someapi"] = "ASD" + + perms["GET_/someapi"] = "ASD" + + perms["PUT_/someapi"] = "ASD" + } -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), +func GetPerm(c *gin.Context) string { + perm, ok := perms[c.Request.Method+"_"+c.Request.URL.Path] + if !ok { + return "" } - - mux.HandleFunc("/someapi", func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "POST": - h_SomeAPI(w, r) - default: - http.Error(w, "Method not allowed", 500) - } - }) - return ret + return perm } -func h_SomeAPI(w http.ResponseWriter, r *http.Request) { +func Build(r *gin.Engine) *gin.Engine { - ctx := r.Context() - ctx = context.WithValue(r.Context(), "REQ", r) - ctx = context.WithValue(ctx, "RES", w) - var req string - if r.Method != http.MethodGet && r.Method != http.MethodHead { - err := json.NewDecoder(r.Body).Decode(&req) + r.DELETE("/someapi", func(c *gin.Context) { + var req *crypto.Hash + c.BindJSON(req) + res, err := SomeAPI2(c.Request.Context(), req) if err != nil { - http.Error(w, err.Error(), 500) - return + c.Error(err) } - } + c.JSON(200, res) + }) + r.GET("/someapi", func(c *gin.Context) { + var req string + c.BindJSON(&req) + res, err := SomeGET(c.Request.Context(), req) + if err != nil { + c.Error(err) + } + c.JSON(200, res) + }) + r.POST("/someapi", func(c *gin.Context) { + var req string + c.BindJSON(&req) + res, err := SomeAPI(c.Request.Context(), req) + if err != nil { + c.Error(err) + } + c.JSON(200, res) + }) + r.PUT("/someapi", func(c *gin.Context) { + var req string + c.BindJSON(&req) + res, err := SomePUT(c.Request.Context(), req) + if err != nil { + c.Error(err) + } + c.JSON(200, res) + }) - 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 - } + return r } diff --git a/test/goapi/test.go b/test/goapi/test.go index b0417d8..e14aa8d 100644 --- a/test/goapi/test.go +++ b/test/goapi/test.go @@ -35,11 +35,13 @@ func SomeAPI(ctx context.Context, s string) (out string, err error) { @API @PATH: /someapi @PERM: ASD -@VERB: PUT +@VERB: GET */ -//func SomeAPI2(ctx context.Context, s dcsession.Session) ([]string, error) { -// return nil, nil -//} +func SomeGET(ctx context.Context, s string) (out string, err error) { + print("Got:" + s) + out = time.Now().String() + " - Hey Ya!" + return +} /* @API @@ -47,6 +49,18 @@ func SomeAPI(ctx context.Context, s string) (out string, err error) { @PERM: ASD @VERB: PUT */ -//func SomeAPI3(ctx context.Context, a time.Time) ([]string, error) { -// return nil, nil -//} +func SomePUT(ctx context.Context, s string) (out string, err error) { + print("Got:" + s) + out = time.Now().String() + " - Hey Ya!" + return +} + +/* +@API +@PATH: /someapi +@PERM: ASD +@VERB: DELETE +*/ +func SomeAPI2(ctx context.Context, s *crypto.Hash) ([]string, error) { + return nil, nil +} diff --git a/test/gocli/cli.go b/test/gocli/cli.go new file mode 100644 index 0000000..5567bb6 --- /dev/null +++ b/test/gocli/cli.go @@ -0,0 +1,117 @@ +package goapi + +import ( + "bytes" + "crypto" + "encoding/json" + "errors" + "io/ioutil" + "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 +} + +type AStr struct { + Arrofpstr []string + + City string + + Country string + + HouseNumber int64 + + IsCondo bool + + Recursive map[string]AStr + + Some crypto.Decrypter + + SomeWeirdTest string + + When time.Time +} + +func SomeAPI(req string) (res string, err error) { + var dec *json.Decoder + dec, err = invoke("POST", "/someapi", req) + if err != nil { + return + } + var ret string + err = dec.Decode(ret) + return ret, err +} + +func SomeAPI2(req crypto.Hash) (res []string, err error) { + var dec *json.Decoder + dec, err = invoke("DELETE", "/someapi", req) + if err != nil { + return + } + var ret []string + err = dec.Decode(ret) + return ret, err +} + +func SomeGET(req string) (res string, err error) { + var dec *json.Decoder + dec, err = invoke("GET", "/someapi", req) + if err != nil { + return + } + var ret string + err = dec.Decode(ret) + return ret, err +} + +func SomePUT(req string) (res string, err error) { + var dec *json.Decoder + dec, err = invoke("PUT", "/someapi", req) + if err != nil { + return + } + var ret string + err = dec.Decode(ret) + return ret, err +} diff --git a/test/main.go b/test/main.go index e3f683d..f0671f6 100644 --- a/test/main.go +++ b/test/main.go @@ -1,12 +1,12 @@ package main import ( + "github.com/gin-gonic/gin" "go.digitalcircle.com.br/tools/apigen/test/goapi" - "net/http" ) func main() { - aapi := goapi.Init() - http.DefaultServeMux.HandleFunc("/", aapi.Mux.ServeHTTP) - http.ListenAndServe(":8080", nil) + g := gin.Default() + goapi.Build(g) + g.Run() }