Merge pull request #439 from beego/develop

v1.8.4
This commit is contained in:
astaxie 2017-05-19 21:19:23 +08:00 committed by GitHub
commit 9e4a43f08a
11 changed files with 245 additions and 72 deletions

View File

@ -10,10 +10,13 @@ install:
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u honnef.co/go/tools/cmd/unused
- go get -u github.com/mdempsky/unconvert
- go get -u github.com/gordonklaus/ineffassign
script:
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
- go vet $(go list ./... | grep -v /vendor/)
- structcheck $(go list ./... | grep -v /vendor/)
- gosimple -ignore "$(cat gosimple.ignore)" $(go list ./... | grep -v /vendor/)
- staticcheck $(go list ./... | grep -v /vendor/)
- staticcheck -ignore "$(cat staticcheck.ignore)" $(go list ./... | grep -v /vendor/)
- unused $(go list ./... | grep -v /vendor/)
- unconvert $(go list ./... | grep -v /vendor/)
- ineffassign .

View File

@ -1,3 +1,5 @@
VERSION = $(shell grep 'const version' cmd/commands/version/version.go | sed -E 's/.*"(.+)"$$/v\1/')
.PHONY: all test clean build install
GOFLAGS ?= $(GOFLAGS:)
@ -18,3 +20,10 @@ bench: install
clean:
go clean $(GOFLAGS) -i ./...
publish:
mkdir -p bin/$(VERSION)
cd bin/$(VERSION)
xgo -v -x --targets="windows/*,darwin/*,linux/386,linux/amd64,linux/arm-5,linux/arm64" -out bee_$(VERSION) github.com/beego/bee
cd ..
ghr -u beego -r bee $(VERSION) $(VERSION)

View File

@ -417,7 +417,7 @@ func packDirectory(output io.Writer, excludePrefix []string, excludeSuffix []str
func packApp(cmd *commands.Command, args []string) int {
output := cmd.Out()
curPath, _ := os.Getwd()
thePath := ""
var thePath string
nArgs := []string{}
has := false

View File

@ -57,7 +57,7 @@ Prints the current Bee, Beego and Go version alongside the platform information.
}
var outputFormat string
const version = "1.8.3"
const version = "1.8.4"
func init() {
fs := flag.NewFlagSet("version", flag.ContinueOnError)

View File

@ -503,9 +503,7 @@ func (mysqlDB *MysqlDB) GetColumns(db *sql.DB, table *Table, blackList map[strin
// GetGoDataType maps an SQL data type to Golang data type
func (*MysqlDB) GetGoDataType(sqlType string) (string, error) {
var typeMapping = map[string]string{}
typeMapping = typeMappingMysql
if v, ok := typeMapping[sqlType]; ok {
if v, ok := typeMappingMysql[sqlType]; ok {
return v, nil
}
return "", fmt.Errorf("data type '%s' not found", sqlType)
@ -766,7 +764,7 @@ func writeModelFiles(tables []*Table, mPath string, selectedTables map[string]bo
continue
}
}
template := ""
var template string
if tb.Pk == "" {
template = StructModelTPL
} else {

View File

@ -369,7 +369,7 @@ func writeHproseModelFiles(tables []*Table, mPath string, selectedTables map[str
continue
}
}
template := ""
var template string
if tb.Pk == "" {
template = HproseStructModelTPL
} else {

View File

@ -184,11 +184,62 @@ func GenerateDocs(curpath string) {
rootapi.Schemes = strings.Split(strings.TrimSpace(s[len("@Schemes"):]), ",")
} else if strings.HasPrefix(s, "@Host") {
rootapi.Host = strings.TrimSpace(s[len("@Host"):])
} else if strings.HasPrefix(s, "@SecurityDefinition") {
if len(rootapi.SecurityDefinitions) == 0 {
rootapi.SecurityDefinitions = make(map[string]swagger.Security)
}
var out swagger.Security
p := getparams(strings.TrimSpace(s[len("@SecurityDefinition"):]))
if len(p) < 2 {
beeLogger.Log.Fatalf("Not enough params for security: %d\n", len(p))
}
out.Type = p[1]
switch out.Type {
case "oauth2":
if len(p) < 6 {
beeLogger.Log.Fatalf("Not enough params for oauth2: %d\n", len(p))
}
if !(p[3] == "implicit" || p[3] == "password" || p[3] == "application" || p[3] == "accessCode") {
beeLogger.Log.Fatalf("Unknown flow type: %s. Possible values are `implicit`, `password`, `application` or `accessCode`.\n", p[1])
}
out.AuthorizationURL = p[2]
out.Flow = p[3]
if len(p)%2 != 0 {
out.Description = strings.Trim(p[len(p)-1], `" `)
}
out.Scopes = make(map[string]string)
for i := 4; i < len(p)-1; i += 2 {
out.Scopes[p[i]] = strings.Trim(p[i+1], `" `)
}
case "apiKey":
if len(p) < 4 {
beeLogger.Log.Fatalf("Not enough params for apiKey: %d\n", len(p))
}
if !(p[3] == "header" || p[3] == "query") {
beeLogger.Log.Fatalf("Unknown in type: %s. Possible values are `query` or `header`.\n", p[4])
}
out.Name = p[2]
out.In = p[3]
if len(p) > 4 {
out.Description = strings.Trim(p[4], `" `)
}
case "basic":
if len(p) > 2 {
out.Description = strings.Trim(p[2], `" `)
}
default:
beeLogger.Log.Fatalf("Unknown security type: %s. Possible values are `oauth2`, `apiKey` or `basic`.\n", p[1])
}
rootapi.SecurityDefinitions[p[0]] = out
} else if strings.HasPrefix(s, "@Security") {
if len(rootapi.Security) == 0 {
rootapi.Security = make([]map[string][]string, 0)
}
rootapi.Security = append(rootapi.Security, getSecurity(s))
}
}
}
}
// Analyse controller package
for _, im := range f.Imports {
localName := ""
@ -216,7 +267,7 @@ func GenerateDocs(curpath string) {
for _, p := range params {
switch pp := p.(type) {
case *ast.CallExpr:
controllerName := ""
var controllerName string
if selname := pp.Fun.(*ast.SelectorExpr).Sel.String(); selname == "NSNamespace" {
s, params := analyseNewNamespace(pp)
for _, sp := range params {
@ -298,12 +349,10 @@ func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
}
if apis, ok := controllerList[cname]; ok {
for rt, item := range apis {
tag := ""
tag := cname
if baseurl != "" {
rt = baseurl + rt
tag = strings.Trim(baseurl, "/")
} else {
tag = cname
}
if item.Get != nil {
item.Get.Tags = []string{tag}
@ -395,7 +444,7 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) {
if specDecl.Recv != nil && len(specDecl.Recv.List) > 0 {
if t, ok := specDecl.Recv.List[0].Type.(*ast.StarExpr); ok {
// Parse controller method
parserComments(specDecl.Doc, specDecl.Name.String(), fmt.Sprint(t.X), pkgpath)
parserComments(specDecl, fmt.Sprint(t.X), pkgpath)
}
}
case *ast.GenDecl:
@ -448,12 +497,16 @@ func peekNextSplitString(ss string) (s string, spacePos int) {
}
// parse the func comments
func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpath string) error {
func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
var routerPath string
var HTTPMethod string
opts := swagger.Operation{
Responses: make(map[string]swagger.Response),
}
funcName := f.Name.String()
comments := f.Doc
funcParamMap := buildParamMap(f.Type.Params)
//TODO: resultMap := buildParamMap(f.Type.Results)
if comments != nil && comments.List != nil {
for _, c := range comments.List {
t := strings.TrimSpace(strings.TrimLeft(c.Text, "//"))
@ -526,7 +579,17 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
if len(p) < 4 {
beeLogger.Log.Fatal(controllerName + "_" + funcName + "'s comments @Param should have at least 4 params")
}
para.Name = p[0]
paramNames := strings.SplitN(p[0], "=>", 2)
para.Name = paramNames[0]
funcParamName := para.Name
if len(paramNames) > 1 {
funcParamName = paramNames[1]
}
paramType, ok := funcParamMap[funcParamName]
if ok {
delete(funcParamMap, funcParamName)
}
switch p[1] {
case "query":
fallthrough
@ -555,33 +618,10 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
modelsList[pkgpath+controllerName][typ] = mod
appendModels(pkgpath, controllerName, realTypes)
} else {
isArray := false
paraType := ""
paraFormat := ""
if strings.HasPrefix(typ, "[]") {
typ = typ[2:]
isArray = true
}
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" ||
typ == "array" || typ == "file" {
paraType = typ
} else if sType, ok := basicTypes[typ]; ok {
typeFormat := strings.Split(sType, ":")
paraType = typeFormat[0]
paraFormat = typeFormat[1]
} else {
beeLogger.Log.Warnf("[%s.%s] Unknown param type: %s\n", controllerName, funcName, typ)
}
if isArray {
para.Type = "array"
para.Items = &swagger.ParameterItems{
Type: paraType,
Format: paraFormat,
}
} else {
para.Type = paraType
para.Format = paraFormat
if typ == "auto" {
typ = paramType
}
setParamType(&para, typ, pkgpath, controllerName)
}
switch len(p) {
case 5:
@ -633,10 +673,29 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
opts.Produces = append(opts.Produces, ahtml)
}
}
} else if strings.HasPrefix(t, "@Security") {
if len(opts.Security) == 0 {
opts.Security = make([]map[string][]string, 0)
}
opts.Security = append(opts.Security, getSecurity(t))
}
}
}
if routerPath != "" {
//Go over function parameters which were not mapped and create swagger params for them
for name, typ := range funcParamMap {
para := swagger.Parameter{}
para.Name = name
setParamType(&para, typ, pkgpath, controllerName)
if paramInPath(name, routerPath) {
para.In = "path"
} else {
para.In = "query"
}
opts.Parameters = append(opts.Parameters, para)
}
var item *swagger.Item
if itemList, ok := controllerList[pkgpath+controllerName]; ok {
if it, ok := itemList[routerPath]; !ok {
@ -669,6 +728,91 @@ func parserComments(comments *ast.CommentGroup, funcName, controllerName, pkgpat
return nil
}
func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName string) {
isArray := false
paraType := ""
paraFormat := ""
if strings.HasPrefix(typ, "[]") {
typ = typ[2:]
isArray = true
}
if typ == "string" || typ == "number" || typ == "integer" || typ == "boolean" ||
typ == "array" || typ == "file" {
paraType = typ
} else if sType, ok := basicTypes[typ]; ok {
typeFormat := strings.Split(sType, ":")
paraType = typeFormat[0]
paraFormat = typeFormat[1]
} else {
m, mod, realTypes := getModel(typ)
para.Schema = &swagger.Schema{
Ref: "#/definitions/" + m,
}
if _, ok := modelsList[pkgpath+controllerName]; !ok {
modelsList[pkgpath+controllerName] = make(map[string]swagger.Schema)
}
modelsList[pkgpath+controllerName][typ] = mod
appendModels(pkgpath, controllerName, realTypes)
}
if isArray {
para.Type = "array"
para.Items = &swagger.ParameterItems{
Type: paraType,
Format: paraFormat,
}
} else {
para.Type = paraType
para.Format = paraFormat
}
}
func paramInPath(name, route string) bool {
return strings.HasSuffix(route, ":"+name) ||
strings.Contains(route, ":"+name+"/")
}
func getFunctionParamType(t ast.Expr) string {
switch paramType := t.(type) {
case *ast.Ident:
return paramType.Name
// case *ast.Ellipsis:
// result := getFunctionParamType(paramType.Elt)
// result.array = true
// return result
case *ast.ArrayType:
return "[]" + getFunctionParamType(paramType.Elt)
case *ast.StarExpr:
return getFunctionParamType(paramType.X)
case *ast.SelectorExpr:
return getFunctionParamType(paramType.X) + "." + paramType.Sel.Name
default:
return ""
}
}
func buildParamMap(list *ast.FieldList) map[string]string {
i := 0
result := map[string]string{}
if list != nil {
funcParams := list.List
for _, fparam := range funcParams {
param := getFunctionParamType(fparam.Type)
var paramName string
if len(fparam.Names) > 0 {
paramName = fparam.Names[0].Name
} else {
paramName = fmt.Sprint(i)
i++
}
result[paramName] = param
}
}
return result
}
// analisys params return []string
// @Param query form string true "The email for login"
// [query form string true "The email for login"]
@ -678,7 +822,7 @@ func getparams(str string) []string {
var start bool
var r []string
var quoted int8
for _, c := range []rune(str) {
for _, c := range str {
if unicode.IsSpace(c) && quoted == 0 {
if !start {
continue
@ -749,7 +893,6 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
if st.Fields.List != nil {
m.Properties = make(map[string]swagger.Propertie)
for _, field := range st.Fields.List {
realType := ""
isSlice, realType, sType := typeAnalyser(field)
if (isSlice && isBasicType(realType)) || sType == "object" {
if len(strings.Split(realType, " ")) > 1 {
@ -765,7 +908,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
mp := swagger.Propertie{}
if isSlice {
mp.Type = "array"
if isBasicType(realType) {
if isBasicType(strings.Replace(realType, "[]", "", -1)) {
typeFormat := strings.Split(sType, ":")
mp.Items = &swagger.Propertie{
Type: typeFormat[0],
@ -868,7 +1011,7 @@ func parseObject(d *ast.Object, k string, m *swagger.Schema, realTypes *[]string
func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
if arr, ok := f.Type.(*ast.ArrayType); ok {
if isBasicType(fmt.Sprint(arr.Elt)) {
return false, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)]
return true, fmt.Sprintf("[]%v", arr.Elt), basicTypes[fmt.Sprint(arr.Elt)]
}
if mp, ok := arr.Elt.(*ast.MapType); ok {
return false, fmt.Sprintf("map[%v][%v]", mp.Key, mp.Value), "object"
@ -880,7 +1023,11 @@ func typeAnalyser(f *ast.Field) (isSlice bool, realType, swaggerType string) {
}
switch t := f.Type.(type) {
case *ast.StarExpr:
return false, fmt.Sprint(t.X), "object"
basicType := fmt.Sprint(t.X)
if k, ok := basicTypes[basicType]; ok {
return false, basicType, k
}
return false, basicType, "object"
case *ast.MapType:
val := fmt.Sprintf("%v", t.Value)
if isBasicType(val) {
@ -920,6 +1067,19 @@ func appendModels(pkgpath, controllerName string, realTypes []string) {
}
}
func getSecurity(t string) (security map[string][]string) {
security = make(map[string][]string)
p := getparams(strings.TrimSpace(t[len("@Security"):]))
if len(p) == 0 {
beeLogger.Log.Fatalf("No params for security specified\n")
}
security[p[0]] = make([]string, 0)
for i := 1; i < len(p); i++ {
security[p[0]] = append(security[p[0]], p[i])
}
return
}
func urlReplace(src string) string {
pt := strings.Split(src, "/")
for i, p := range pt {

View File

@ -1,2 +1,3 @@
github.com/beego/bee/cmd/commands/run/*.go:S1024
github.com/beego/bee/cmd/commands/dlv/*.go:S1024
github.com/beego/bee/utils/*.go:S1026

View File

@ -360,7 +360,7 @@ func isParameterChar(b byte) bool {
}
func (cw *colorWriter) Write(p []byte) (int, error) {
r, nw, first, last := 0, 0, 0, 0
var r, nw, first, last int
if cw.mode != DiscardNonColorEscSeq {
cw.state = outsideCsiCode
cw.resetBuffer()

1
staticcheck.ignore Normal file
View File

@ -0,0 +1 @@
github.com/beego/bee/generate/swaggergen/*.go:SA1024

View File

@ -22,19 +22,19 @@ package swagger
// Swagger list the resource
type Swagger struct {
SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"`
Infos Information `json:"info" yaml:"info"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Paths map[string]*Item `json:"paths" yaml:"paths"`
Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"`
SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
Security map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"`
Infos Information `json:"info" yaml:"info"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Paths map[string]*Item `json:"paths" yaml:"paths"`
Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"`
SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
}
// Information Provides metadata about the API. The metadata can be used by the clients if needed.
@ -75,16 +75,17 @@ type Item struct {
// Operation Describes a single API operation on a path.
type Operation struct {
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
}
// Parameter Describes a single operation parameter.
@ -100,7 +101,7 @@ type Parameter struct {
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
}
// A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
// ParameterItems A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
// http://swagger.io/specification/#itemsObject
type ParameterItems struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`