mirror of
https://github.com/beego/bee.git
synced 2024-11-22 15:10:54 +00:00
feat: support multi-namespace to generate API version of swagger
This commit is contained in:
parent
431859968b
commit
ae62cd713b
@ -56,12 +56,17 @@ const (
|
|||||||
astTypeMap = "map"
|
astTypeMap = "map"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultNamespacePrefix = ""
|
||||||
|
|
||||||
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
|
var pkgCache map[string]struct{} //pkg:controller:function:comments comments: key:value
|
||||||
var controllerComments map[string]string
|
var controllerComments map[string]string
|
||||||
var importlist map[string]string
|
var importlist map[string]string
|
||||||
var controllerList map[string]map[string]*swagger.Item //controllername Paths items
|
var controllerList map[string]map[string]*swagger.Item //controllername Paths items
|
||||||
var modelsList map[string]map[string]swagger.Schema
|
var modelsList map[string]map[string]swagger.Schema
|
||||||
var rootapi swagger.Swagger
|
|
||||||
|
var rootapiSingle = false
|
||||||
|
var rootapiDefault swagger.Swagger
|
||||||
|
var rootapiMap map[string]*swagger.Swagger // version, swagger
|
||||||
var astPkgs []*ast.Package
|
var astPkgs []*ast.Package
|
||||||
var pkgLoadedCache map[string]struct{}
|
var pkgLoadedCache map[string]struct{}
|
||||||
|
|
||||||
@ -106,6 +111,7 @@ func init() {
|
|||||||
importlist = make(map[string]string)
|
importlist = make(map[string]string)
|
||||||
controllerList = make(map[string]map[string]*swagger.Item)
|
controllerList = make(map[string]map[string]*swagger.Item)
|
||||||
modelsList = make(map[string]map[string]swagger.Schema)
|
modelsList = make(map[string]map[string]swagger.Schema)
|
||||||
|
rootapiMap = make(map[string]*swagger.Swagger)
|
||||||
astPkgs = make([]*ast.Package, 0)
|
astPkgs = make([]*ast.Package, 0)
|
||||||
pkgLoadedCache = make(map[string]struct{})
|
pkgLoadedCache = make(map[string]struct{})
|
||||||
}
|
}
|
||||||
@ -188,14 +194,21 @@ func GenerateDocs(curpath string) {
|
|||||||
beeLogger.Log.Fatalf("Error while parsing router.go: %s", err)
|
beeLogger.Log.Fatalf("Error while parsing router.go: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rootapi.Infos = swagger.Information{}
|
|
||||||
rootapi.SwaggerVersion = "2.0"
|
|
||||||
|
|
||||||
// Analyse API comments
|
// Analyse API comments
|
||||||
if f.Comments != nil {
|
if f.Comments != nil {
|
||||||
for _, c := range f.Comments {
|
for _, c := range f.Comments {
|
||||||
|
var namespacePrefix string
|
||||||
|
rootapi := &swagger.Swagger{
|
||||||
|
Infos: swagger.Information{},
|
||||||
|
SwaggerVersion: "2.0",
|
||||||
|
}
|
||||||
for _, s := range strings.Split(c.Text(), "\n") {
|
for _, s := range strings.Split(c.Text(), "\n") {
|
||||||
if strings.HasPrefix(s, "@APIVersion") {
|
if strings.HasPrefix(s, "@NamespacePrefix") {
|
||||||
|
namespacePrefix = strings.TrimSpace(s[len("@NamespacePrefix"):])
|
||||||
|
if _, exist := rootapiMap[namespacePrefix]; !exist {
|
||||||
|
rootapiMap[namespacePrefix] = rootapi
|
||||||
|
}
|
||||||
|
} else if strings.HasPrefix(s, "@APIVersion") {
|
||||||
rootapi.Infos.Version = strings.TrimSpace(s[len("@APIVersion"):])
|
rootapi.Infos.Version = strings.TrimSpace(s[len("@APIVersion"):])
|
||||||
} else if strings.HasPrefix(s, "@Title") {
|
} else if strings.HasPrefix(s, "@Title") {
|
||||||
rootapi.Infos.Title = strings.TrimSpace(s[len("@Title"):])
|
rootapi.Infos.Title = strings.TrimSpace(s[len("@Title"):])
|
||||||
@ -279,6 +292,12 @@ func GenerateDocs(curpath string) {
|
|||||||
rootapi.Security = append(rootapi.Security, getSecurity(s))
|
rootapi.Security = append(rootapi.Security, getSecurity(s))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// when namespacePrefix not defined, then add default namespacePrefix
|
||||||
|
if _, exist := rootapiMap[namespacePrefix]; !exist {
|
||||||
|
rootapiSingle = true
|
||||||
|
rootapiDefault = *rootapi
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Analyse controller package
|
// Analyse controller package
|
||||||
@ -289,6 +308,7 @@ func GenerateDocs(curpath string) {
|
|||||||
}
|
}
|
||||||
analyseControllerPkg(localName, im.Path.Value)
|
analyseControllerPkg(localName, im.Path.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range f.Decls {
|
for _, d := range f.Decls {
|
||||||
switch specDecl := d.(type) {
|
switch specDecl := d.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
@ -302,21 +322,43 @@ func GenerateDocs(curpath string) {
|
|||||||
if !selOK || selExpr.Sel.Name != "NewNamespace" {
|
if !selOK || selExpr.Sel.Name != "NewNamespace" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
version, params := analyseNewNamespace(v)
|
version, params := analyseNewNamespace(v)
|
||||||
if rootapi.BasePath == "" && version != "" {
|
var rootapi *swagger.Swagger
|
||||||
|
if rootapiSingle {
|
||||||
|
rootapi = &rootapiDefault
|
||||||
|
} else {
|
||||||
|
_rootapi, rootapiExist := rootapiMap[version]
|
||||||
|
if !rootapiExist {
|
||||||
|
rootapi = &swagger.Swagger{
|
||||||
|
Infos: swagger.Information{},
|
||||||
|
SwaggerVersion: "2.0",
|
||||||
|
}
|
||||||
|
rootapiMap[version] = rootapi
|
||||||
|
} else {
|
||||||
|
rootapi = _rootapi
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rootapiSingle && rootapi.BasePath == "" {
|
||||||
rootapi.BasePath = version
|
rootapi.BasePath = version
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range params {
|
for _, p := range params {
|
||||||
switch pp := p.(type) {
|
switch pp := p.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
var controllerName string
|
var controllerName string
|
||||||
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
|
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
|
||||||
s, params := analyseNewNamespace(pp)
|
s, params := analyseNewNamespace(pp)
|
||||||
|
if rootapiSingle {
|
||||||
|
s = path.Join(version, s)
|
||||||
|
}
|
||||||
|
|
||||||
for _, sp := range params {
|
for _, sp := range params {
|
||||||
switch pp := sp.(type) {
|
switch pp := sp.(type) {
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
|
if pp.Fun.(*ast.SelectorExpr).Sel.String() == "NSInclude" {
|
||||||
controllerName = analyseNSInclude(s, pp)
|
controllerName = analyseNSInclude(s, pp, rootapi)
|
||||||
if v, ok := controllerComments[controllerName]; ok {
|
if v, ok := controllerComments[controllerName]; ok {
|
||||||
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||||
Name: strings.Trim(s, "/"),
|
Name: strings.Trim(s, "/"),
|
||||||
@ -327,7 +369,7 @@ func GenerateDocs(curpath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if selname == "NSInclude" {
|
} else if selname == "NSInclude" {
|
||||||
controllerName = analyseNSInclude("", pp)
|
controllerName = analyseNSInclude("", pp, rootapi)
|
||||||
if v, ok := controllerComments[controllerName]; ok {
|
if v, ok := controllerComments[controllerName]; ok {
|
||||||
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
rootapi.Tags = append(rootapi.Tags, swagger.Tag{
|
||||||
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag
|
Name: controllerName, // if the NSInclude has no prefix, we use the controllername as the tag
|
||||||
@ -344,26 +386,39 @@ func GenerateDocs(curpath string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
os.Mkdir(path.Join(curpath, "swagger"), 0755)
|
|
||||||
fd, err := os.Create(path.Join(curpath, "swagger", "swagger.json"))
|
if rootapiSingle {
|
||||||
if err != nil {
|
rootapiMap[defaultNamespacePrefix] = &rootapiDefault
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
fdyml, err := os.Create(path.Join(curpath, "swagger", "swagger.yml"))
|
|
||||||
if err != nil {
|
for version, api := range rootapiMap {
|
||||||
panic(err)
|
api.Definitions = rootapiDefault.Definitions
|
||||||
}
|
|
||||||
defer fdyml.Close()
|
dir := path.Join(curpath, "swagger", version)
|
||||||
defer fd.Close()
|
os.MkdirAll(dir, 0755)
|
||||||
dt, err := json.MarshalIndent(rootapi, "", " ")
|
|
||||||
dtyml, erryml := yaml.Marshal(rootapi)
|
fd, err := os.Create(path.Join(dir, "swagger.json"))
|
||||||
if err != nil || erryml != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
_, err = fd.Write(dt)
|
defer fd.Close()
|
||||||
_, erryml = fdyml.Write(dtyml)
|
|
||||||
if err != nil || erryml != nil {
|
fdyml, err := os.Create(path.Join(dir, "swagger.yml"))
|
||||||
panic(err)
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer fdyml.Close()
|
||||||
|
|
||||||
|
dt, err := json.MarshalIndent(api, "", " ")
|
||||||
|
dtyml, erryml := yaml.Marshal(api)
|
||||||
|
if err != nil || erryml != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
_, err = fd.Write(dt)
|
||||||
|
_, erryml = fdyml.Write(dtyml)
|
||||||
|
if err != nil || erryml != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +437,7 @@ func analyseNewNamespace(ce *ast.CallExpr) (first string, others []ast.Expr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
|
func analyseNSInclude(baseurl string, ce *ast.CallExpr, rootapi *swagger.Swagger) string {
|
||||||
cname := ""
|
cname := ""
|
||||||
for _, p := range ce.Args {
|
for _, p := range ce.Args {
|
||||||
var x *ast.SelectorExpr
|
var x *ast.SelectorExpr
|
||||||
@ -964,16 +1019,16 @@ L:
|
|||||||
|
|
||||||
if m.Title == "" {
|
if m.Title == "" {
|
||||||
// Don't log when error has already been logged
|
// Don't log when error has already been logged
|
||||||
if _, found := rootapi.Definitions[str]; !found {
|
if _, found := rootapiDefault.Definitions[str]; !found {
|
||||||
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
beeLogger.Log.Warnf("Cannot find the object: %s", str)
|
||||||
}
|
}
|
||||||
m.Title = objectname
|
m.Title = objectname
|
||||||
// TODO remove when all type have been supported
|
// TODO remove when all type have been supported
|
||||||
}
|
}
|
||||||
if len(rootapi.Definitions) == 0 {
|
if len(rootapiDefault.Definitions) == 0 {
|
||||||
rootapi.Definitions = make(map[string]swagger.Schema)
|
rootapiDefault.Definitions = make(map[string]swagger.Schema)
|
||||||
}
|
}
|
||||||
rootapi.Definitions[str] = m
|
rootapiDefault.Definitions[str] = m
|
||||||
return str, m, realTypes
|
return str, m, realTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -992,7 +1047,7 @@ func parseObject(imports []*ast.ImportSpec, d *ast.Object, k string, m *swagger.
|
|||||||
m.Format = typeFormat[0]
|
m.Format = typeFormat[0]
|
||||||
} else {
|
} else {
|
||||||
objectName := packageName + "." + fmt.Sprint(t.Elt)
|
objectName := packageName + "." + fmt.Sprint(t.Elt)
|
||||||
if _, ok := rootapi.Definitions[objectName]; !ok {
|
if _, ok := rootapiDefault.Definitions[objectName]; !ok {
|
||||||
objectName, _, _ = getModel(objectName)
|
objectName, _, _ = getModel(objectName)
|
||||||
}
|
}
|
||||||
m.Items = &swagger.Schema{
|
m.Items = &swagger.Schema{
|
||||||
|
Loading…
Reference in New Issue
Block a user