mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-07 15:10:01 +08:00
goctl features of 1.8.4-alpha (#4849)
This commit is contained in:
1
tools/goctl/.gitignore
vendored
Normal file
1
tools/goctl/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
dist
|
||||||
@@ -77,6 +77,7 @@ func init() {
|
|||||||
goCmdFlags.StringVar(&gogen.VarStringRemote, "remote")
|
goCmdFlags.StringVar(&gogen.VarStringRemote, "remote")
|
||||||
goCmdFlags.StringVar(&gogen.VarStringBranch, "branch")
|
goCmdFlags.StringVar(&gogen.VarStringBranch, "branch")
|
||||||
goCmdFlags.BoolVar(&gogen.VarBoolWithTest, "test")
|
goCmdFlags.BoolVar(&gogen.VarBoolWithTest, "test")
|
||||||
|
goCmdFlags.BoolVar(&gogen.VarBoolTypeGroup, "type-group")
|
||||||
goCmdFlags.StringVarWithDefaultValue(&gogen.VarStringStyle, "style", config.DefaultFormat)
|
goCmdFlags.StringVarWithDefaultValue(&gogen.VarStringStyle, "style", config.DefaultFormat)
|
||||||
|
|
||||||
javaCmdFlags.StringVar(&javagen.VarStringDir, "dir")
|
javaCmdFlags.StringVar(&javagen.VarStringDir, "dir")
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ var (
|
|||||||
// VarStringStyle describes the style of output files.
|
// VarStringStyle describes the style of output files.
|
||||||
VarStringStyle string
|
VarStringStyle string
|
||||||
VarBoolWithTest bool
|
VarBoolWithTest bool
|
||||||
|
// VarBoolTypeGroup describes whether to group types.
|
||||||
|
VarBoolTypeGroup bool
|
||||||
)
|
)
|
||||||
|
|
||||||
// GoCommand gen go project files from command line
|
// GoCommand gen go project files from command line
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import (
|
|||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/collection"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
apiutil "github.com/zeromicro/go-zero/tools/goctl/api/util"
|
apiutil "github.com/zeromicro/go-zero/tools/goctl/api/util"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/config"
|
"github.com/zeromicro/go-zero/tools/goctl/config"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/internal/version"
|
"github.com/zeromicro/go-zero/tools/goctl/internal/version"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/env"
|
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/util"
|
"github.com/zeromicro/go-zero/tools/goctl/util"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/util/format"
|
"github.com/zeromicro/go-zero/tools/goctl/util/format"
|
||||||
)
|
)
|
||||||
@@ -41,53 +41,116 @@ func BuildTypes(types []spec.Type) (string, error) {
|
|||||||
return builder.String(), nil
|
return builder.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeTypeFromDefault(tp spec.Type, group string, groupTypes map[string]map[string]spec.Type) map[string]map[string]spec.Type {
|
func getTypeName(tp spec.Type) string {
|
||||||
|
if tp == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
switch val := tp.(type) {
|
switch val := tp.(type) {
|
||||||
case spec.DefineStruct:
|
case spec.DefineStruct:
|
||||||
typeName := util.Title(tp.Name())
|
typeName := util.Title(tp.Name())
|
||||||
defaultGroups, ok := groupTypes[groupTypeDefault]
|
return typeName
|
||||||
if ok {
|
|
||||||
delete(defaultGroups, typeName)
|
|
||||||
types, ok := groupTypes[group]
|
|
||||||
if !ok {
|
|
||||||
types = make(map[string]spec.Type)
|
|
||||||
}
|
|
||||||
types[typeName] = tp
|
|
||||||
groupTypes[group] = types
|
|
||||||
}
|
|
||||||
groupTypes[groupTypeDefault] = defaultGroups
|
|
||||||
case spec.PointerType:
|
case spec.PointerType:
|
||||||
groupTypes = removeTypeFromDefault(val.Type, group, groupTypes)
|
return getTypeName(val.Type)
|
||||||
case spec.ArrayType:
|
case spec.ArrayType:
|
||||||
groupTypes = removeTypeFromDefault(val.Value, group, groupTypes)
|
return getTypeName(val.Value)
|
||||||
}
|
}
|
||||||
return groupTypes
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func genTypesWithGroup(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
func genTypesWithGroup(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||||
groupTypes := make(map[string]map[string]spec.Type)
|
groupTypes := make(map[string]map[string]spec.Type)
|
||||||
for _, v := range api.Types {
|
typesBelongToFiles := make(map[string]*collection.Set)
|
||||||
types, ok := groupTypes[groupTypeDefault]
|
|
||||||
if !ok {
|
|
||||||
types = make(map[string]spec.Type)
|
|
||||||
}
|
|
||||||
types[util.Title(v.Name())] = v
|
|
||||||
groupTypes[groupTypeDefault] = types
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range api.Service.Groups {
|
for _, v := range api.Service.Groups {
|
||||||
group := v.GetAnnotation(groupProperty)
|
group := v.GetAnnotation(groupProperty)
|
||||||
if len(group) == 0 {
|
if len(group) == 0 {
|
||||||
|
group = groupTypeDefault
|
||||||
|
}
|
||||||
|
// convert filepath to Identifier name spec.
|
||||||
|
group = strings.TrimPrefix(group, "/")
|
||||||
|
group = strings.TrimSuffix(group, "/")
|
||||||
|
group = util.SafeString(group)
|
||||||
|
for _, v := range v.Routes {
|
||||||
|
requestTypeName := getTypeName(v.RequestType)
|
||||||
|
responseTypeName := getTypeName(v.ResponseType)
|
||||||
|
requestTypeFileSet, ok := typesBelongToFiles[requestTypeName]
|
||||||
|
if !ok {
|
||||||
|
requestTypeFileSet = collection.NewSet()
|
||||||
|
}
|
||||||
|
if len(requestTypeName) > 0 {
|
||||||
|
requestTypeFileSet.AddStr(group)
|
||||||
|
typesBelongToFiles[requestTypeName] = requestTypeFileSet
|
||||||
|
}
|
||||||
|
|
||||||
|
responseTypeFileSet, ok := typesBelongToFiles[responseTypeName]
|
||||||
|
if !ok {
|
||||||
|
responseTypeFileSet = collection.NewSet()
|
||||||
|
}
|
||||||
|
if len(responseTypeName) > 0 {
|
||||||
|
responseTypeFileSet.AddStr(group)
|
||||||
|
typesBelongToFiles[responseTypeName] = responseTypeFileSet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typesInOneFile := make(map[string]*collection.Set)
|
||||||
|
for typeName, fileSet := range typesBelongToFiles {
|
||||||
|
count := fileSet.Count()
|
||||||
|
switch {
|
||||||
|
case count == 0: // it means there has no structure type or no request/response body
|
||||||
|
continue
|
||||||
|
case count == 1: // it means a structure type used in only one group.
|
||||||
|
groupName := fileSet.KeysStr()[0]
|
||||||
|
typeSet, ok := typesInOneFile[groupName]
|
||||||
|
if !ok {
|
||||||
|
typeSet = collection.NewSet()
|
||||||
|
}
|
||||||
|
typeSet.AddStr(typeName)
|
||||||
|
typesInOneFile[groupName] = typeSet
|
||||||
|
default: // it means this type is used in multiple groups.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, v := range v.Routes {
|
}
|
||||||
if v.RequestType != nil {
|
|
||||||
groupTypes = removeTypeFromDefault(v.RequestType, group, groupTypes)
|
for _, v := range api.Types {
|
||||||
}
|
typeName := util.Title(v.Name())
|
||||||
if v.ResponseType != nil {
|
groupSet, ok := typesBelongToFiles[typeName]
|
||||||
groupTypes = removeTypeFromDefault(v.ResponseType, group, groupTypes)
|
var typeCount int
|
||||||
}
|
if !ok {
|
||||||
|
typeCount = 0
|
||||||
|
} else {
|
||||||
|
typeCount = groupSet.Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if typeCount == 0 { // not belong to any group
|
||||||
|
types, ok := groupTypes[groupTypeDefault]
|
||||||
|
if !ok {
|
||||||
|
types = make(map[string]spec.Type)
|
||||||
|
}
|
||||||
|
types[typeName] = v
|
||||||
|
groupTypes[groupTypeDefault] = types
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeCount == 1 { // belong to one group
|
||||||
|
groupName := groupSet.KeysStr()[0]
|
||||||
|
types, ok := groupTypes[groupName]
|
||||||
|
if !ok {
|
||||||
|
types = make(map[string]spec.Type)
|
||||||
|
}
|
||||||
|
types[typeName] = v
|
||||||
|
groupTypes[groupName] = types
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// belong to multiple groups
|
||||||
|
types, ok := groupTypes[groupTypeDefault]
|
||||||
|
if !ok {
|
||||||
|
types = make(map[string]spec.Type)
|
||||||
|
}
|
||||||
|
types[typeName] = v
|
||||||
|
groupTypes[groupTypeDefault] = types
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for group, typeGroup := range groupTypes {
|
for group, typeGroup := range groupTypes {
|
||||||
@@ -142,7 +205,7 @@ func writeTypes(dir, baseFilename string, cfg *config.Config, types []spec.Type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
func genTypes(dir string, cfg *config.Config, api *spec.ApiSpec) error {
|
||||||
if env.UseExperimental() {
|
if VarBoolTypeGroup {
|
||||||
return genTypesWithGroup(dir, cfg, api)
|
return genTypesWithGroup(dir, cfg, api)
|
||||||
}
|
}
|
||||||
return writeTypes(dir, typesFile, cfg, api.Types)
|
return writeTypes(dir, typesFile, cfg, api.Types)
|
||||||
|
|||||||
@@ -7,15 +7,6 @@ import (
|
|||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
func hasKey(properties map[string]string, key string) bool {
|
|
||||||
if len(properties) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
md := metadata.New(properties)
|
|
||||||
_, ok := md[key]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBoolFromKVOrDefault(properties map[string]string, key string, def bool) bool {
|
func getBoolFromKVOrDefault(properties map[string]string, key string, def bool) bool {
|
||||||
if len(properties) == 0 {
|
if len(properties) == 0 {
|
||||||
return def
|
return def
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func Test_getListFromInfoOrDefault(t *testing.T) {
|
|||||||
"empty": `""`,
|
"empty": `""`,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, []string{"a", "b", "c"}, getListFromInfoOrDefault(properties, "list", []string{"default"}))
|
assert.Equal(t, []string{"a", " b", " c"}, getListFromInfoOrDefault(properties, "list", []string{"default"}))
|
||||||
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(properties, "empty", []string{"default"}))
|
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(properties, "empty", []string{"default"}))
|
||||||
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(properties, "missing", []string{"default"}))
|
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(properties, "missing", []string{"default"}))
|
||||||
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(nil, "nil", []string{"default"}))
|
assert.Equal(t, []string{"default"}, getListFromInfoOrDefault(nil, "nil", []string{"default"}))
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/parser"
|
"github.com/zeromicro/go-zero/tools/goctl/pkg/parser/api/parser"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tagHeader = "header"
|
tagHeader = "header"
|
||||||
tagPath = "path"
|
tagPath = "path"
|
||||||
tagForm = "form"
|
tagForm = "form"
|
||||||
tagJson = "json"
|
tagJson = "json"
|
||||||
defFlag = "default="
|
defFlag = "default="
|
||||||
enumFlag = "options="
|
enumFlag = "options="
|
||||||
rangeFlag = "range="
|
rangeFlag = "range="
|
||||||
exampleFlag = "example="
|
exampleFlag = "example="
|
||||||
|
optionalFlag = "optional"
|
||||||
|
|
||||||
paramsInHeader = "header"
|
paramsInHeader = "header"
|
||||||
paramsInPath = "path"
|
paramsInPath = "path"
|
||||||
@@ -27,6 +28,36 @@ const (
|
|||||||
applicationJson = "application/json"
|
applicationJson = "application/json"
|
||||||
applicationForm = "application/x-www-form-urlencoded"
|
applicationForm = "application/x-www-form-urlencoded"
|
||||||
schemeHttps = "https"
|
schemeHttps = "https"
|
||||||
defaultHost = "127.0.0.1"
|
|
||||||
defaultBasePath = "/"
|
defaultBasePath = "/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
propertyKeyUseDefinitions = "useDefinitions"
|
||||||
|
propertyKeyExternalDocsDescription = "externalDocsDescription"
|
||||||
|
propertyKeyExternalDocsURL = "externalDocsURL"
|
||||||
|
propertyKeyTitle = "title"
|
||||||
|
propertyKeyTermsOfService = "termsOfService"
|
||||||
|
propertyKeyDescription = "description"
|
||||||
|
propertyKeyVersion = "version"
|
||||||
|
propertyKeyContactName = "contactName"
|
||||||
|
propertyKeyContactURL = "contactURL"
|
||||||
|
propertyKeyContactEmail = "contactEmail"
|
||||||
|
propertyKeyLicenseName = "licenseName"
|
||||||
|
propertyKeyLicenseURL = "licenseURL"
|
||||||
|
propertyKeyProduces = "produces"
|
||||||
|
propertyKeyConsumes = "consumes"
|
||||||
|
propertyKeySchemes = "schemes"
|
||||||
|
propertyKeyTags = "tags"
|
||||||
|
propertyKeySummary = "summary"
|
||||||
|
propertyKeyDeprecated = "deprecated"
|
||||||
|
propertyKeyPrefix = "prefix"
|
||||||
|
propertyKeyAuthType = "authType"
|
||||||
|
propertyKeyHost = "host"
|
||||||
|
propertyKeyBasePath = "basePath"
|
||||||
|
propertyKeyWrapCodeMsg = "wrapCodeMsg"
|
||||||
|
propertyKeyBizCodeEnumDescription = "bizCodeEnumDescription"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultValueOfPropertyUseDefinition = false
|
||||||
|
)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func consumesFromTypeOrDef(method string, tp spec.Type) []string {
|
func consumesFromTypeOrDef(ctx Context, method string, tp spec.Type) []string {
|
||||||
if strings.EqualFold(method, http.MethodGet) {
|
if strings.EqualFold(method, http.MethodGet) {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,7 @@ func consumesFromTypeOrDef(method string, tp spec.Type) []string {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return []string{}
|
return []string{}
|
||||||
}
|
}
|
||||||
if typeContainsTag(structType, tagJson) {
|
if typeContainsTag(ctx, structType, tagJson) {
|
||||||
return []string{applicationJson}
|
return []string{applicationJson}
|
||||||
}
|
}
|
||||||
return []string{applicationForm}
|
return []string{applicationForm}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ func TestConsumesFromTypeOrDef(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := consumesFromTypeOrDef(tt.method, tt.tp)
|
result := consumesFromTypeOrDef(testingContext(t), tt.method, tt.tp)
|
||||||
assert.Equal(t, tt.expected, result)
|
assert.Equal(t, tt.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
28
tools/goctl/api/swagger/context.go
Normal file
28
tools/goctl/api/swagger/context.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package swagger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Context struct {
|
||||||
|
UseDefinitions bool
|
||||||
|
WrapCodeMsg bool
|
||||||
|
BizCodeEnumDescription string
|
||||||
|
}
|
||||||
|
|
||||||
|
func testingContext(_ *testing.T) Context {
|
||||||
|
return Context{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contextFromApi(info spec.Info) Context {
|
||||||
|
if len(info.Properties) == 0 {
|
||||||
|
return Context{}
|
||||||
|
}
|
||||||
|
return Context{
|
||||||
|
UseDefinitions: getBoolFromKVOrDefault(info.Properties, propertyKeyUseDefinitions, defaultValueOfPropertyUseDefinition),
|
||||||
|
WrapCodeMsg: getBoolFromKVOrDefault(info.Properties, propertyKeyWrapCodeMsg, false),
|
||||||
|
BizCodeEnumDescription: getStringFromKVOrDefault(info.Properties, propertyKeyBizCodeEnumDescription, "business code"),
|
||||||
|
}
|
||||||
|
}
|
||||||
32
tools/goctl/api/swagger/definition.go
Normal file
32
tools/goctl/api/swagger/definition.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package swagger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func definitionsFromTypes(ctx Context, types []apiSpec.Type) spec.Definitions {
|
||||||
|
if !ctx.UseDefinitions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
definitions := make(spec.Definitions)
|
||||||
|
for _, tp := range types {
|
||||||
|
typeName := tp.Name()
|
||||||
|
definitions[typeName] = schemaFromType(ctx, tp)
|
||||||
|
}
|
||||||
|
return definitions
|
||||||
|
}
|
||||||
|
|
||||||
|
func schemaFromType(ctx Context, tp apiSpec.Type) spec.Schema {
|
||||||
|
p, r := propertiesFromType(ctx, tp)
|
||||||
|
props := spec.SchemaProps{
|
||||||
|
Type: typeFromGoType(ctx, tp),
|
||||||
|
Properties: p,
|
||||||
|
AdditionalProperties: mapFromGoType(ctx, tp),
|
||||||
|
Items: itemsFromGoType(ctx, tp),
|
||||||
|
Required: r,
|
||||||
|
}
|
||||||
|
return spec.Schema{
|
||||||
|
SchemaProps: props,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,15 +12,16 @@ info (
|
|||||||
licenseURL: "https://github.com/zeromicro/go-zero" // licenseURL corresponding to Swagger
|
licenseURL: "https://github.com/zeromicro/go-zero" // licenseURL corresponding to Swagger
|
||||||
consumes: "application/json" // consumes corresponding to Swagger,default value is `application/json`
|
consumes: "application/json" // consumes corresponding to Swagger,default value is `application/json`
|
||||||
produces: "application/json" // produces corresponding to Swagger,default value is `application/json`
|
produces: "application/json" // produces corresponding to Swagger,default value is `application/json`
|
||||||
schemes: "https" // schemes corresponding to Swagger,default value is `https``
|
schemes: "http,https" // schemes corresponding to Swagger,default value is `https``
|
||||||
host: "example.com" // host corresponding to Swagger,default value is `127.0.0.1`
|
host: "example.com" // host corresponding to Swagger,default value is `127.0.0.1`
|
||||||
basePath: "/v1" // basePath corresponding to Swagger,default value is `/`
|
basePath: "/v1" // basePath corresponding to Swagger,default value is `/`
|
||||||
wrapCodeMsg: "true" // to wrap in the universal code-msg structure, like {"code":0,"msg":"OK","data":$data}
|
wrapCodeMsg: true // to wrap in the universal code-msg structure, like {"code":0,"msg":"OK","data":$data}
|
||||||
bizCodeEnumDescription: "1001-User not login<br>1002-User permission denied" // enums of business error codes, in JSON format, with the key being the business error code and the value being the description of that error code. This only takes effect when wrapCodeMsg is set to true.
|
bizCodeEnumDescription: "1001-User not login<br>1002-User permission denied" // enums of business error codes, in JSON format, with the key being the business error code and the value being the description of that error code. This only takes effect when wrapCodeMsg is set to true.
|
||||||
// securityDefinitionsFromJson is a custom authentication configuration, and the JSON content will be directly inserted into the securityDefinitions of Swagger.
|
// securityDefinitionsFromJson is a custom authentication configuration, and the JSON content will be directly inserted into the securityDefinitions of Swagger.
|
||||||
// Format reference: https://swagger.io/specification/v2/#security-definitions-object
|
// Format reference: https://swagger.io/specification/v2/#security-definitions-object
|
||||||
// You can declare authType in the @server of the API to specify the authentication type used for its routes.
|
// You can declare authType in the @server of the API to specify the authentication type used for its routes.
|
||||||
securityDefinitionsFromJson: `{"apiKey":{"description":"apiKey type description","type":"apiKey","name":"x-api-key","in":"header"}}`
|
securityDefinitionsFromJson: `{"apiKey":{"description":"apiKey type description","type":"apiKey","name":"x-api-key","in":"header"}}`
|
||||||
|
useDefinitions: true // if set true, the definitions will be generated in the swagger.json for response body or json request body file, and the models will be referenced in the API.
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
4980
tools/goctl/api/swagger/example/example.swagger.json
Normal file
4980
tools/goctl/api/swagger/example/example.swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,15 +12,16 @@ info (
|
|||||||
licenseURL: "https://github.com/zeromicro/go-zero" // 对应 swagger 的 licenseURL
|
licenseURL: "https://github.com/zeromicro/go-zero" // 对应 swagger 的 licenseURL
|
||||||
consumes: "application/json" // 对应 swagger 的 consumes,不填默认为 application/json
|
consumes: "application/json" // 对应 swagger 的 consumes,不填默认为 application/json
|
||||||
produces: "application/json" // 对应 swagger 的 produces,不填默认为 application/json
|
produces: "application/json" // 对应 swagger 的 produces,不填默认为 application/json
|
||||||
schemes: "https" // 对应 swagger 的 schemes,不填默认为 https
|
schemes: "http,https" // 对应 swagger 的 schemes,不填默认为 https
|
||||||
host: "example.com" // 对应 swagger 的 host,不填默认为 127.0.0.1
|
host: "example.com" // 对应 swagger 的 host,不填默认为 127.0.0.1
|
||||||
basePath: "/v1" // 对应 swagger 的 basePath,不填默认为 /
|
basePath: "/v1" // 对应 swagger 的 basePath,不填默认为 /
|
||||||
wrapCodeMsg: "true" // 是否用 code-msg 通用响应体,如果开启,则以格式 {"code":0,"msg":"OK","data":$data} 包括响应体
|
wrapCodeMsg: true // 是否用 code-msg 通用响应体,如果开启,则以格式 {"code":0,"msg":"OK","data":$data} 包括响应体
|
||||||
bizCodeEnumDescription: "1001-未登录<br>1002-无权限操作" // 全局业务错误码枚举描述,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 时生效
|
bizCodeEnumDescription: "1001-未登录<br>1002-无权限操作" // 全局业务错误码枚举描述,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 时生效
|
||||||
// securityDefinitionsFromJson 为自定义鉴权配置,json 内容将直接放入 swagger 的 securityDefinitions 中,
|
// securityDefinitionsFromJson 为自定义鉴权配置,json 内容将直接放入 swagger 的 securityDefinitions 中,
|
||||||
// 格式参考 https://swagger.io/specification/v2/#security-definitions-object
|
// 格式参考 https://swagger.io/specification/v2/#security-definitions-object
|
||||||
// 在 api 的 @server 中可声明 authType 来指定其路由使用的鉴权类型
|
// 在 api 的 @server 中可声明 authType 来指定其路由使用的鉴权类型
|
||||||
securityDefinitionsFromJson: `{"apiKey":{"description":"apiKey 类型鉴权自定义","type":"apiKey","name":"x-api-key","in":"header"}}`
|
securityDefinitionsFromJson: `{"apiKey":{"description":"apiKey 类型鉴权自定义","type":"apiKey","name":"x-api-key","in":"header"}}`
|
||||||
|
useDefinitions: true// 开启声明将生成models 进行关联,definitions 仅对响应体和 json 请求体生效
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -52,7 +53,7 @@ type (
|
|||||||
service Swagger {
|
service Swagger {
|
||||||
@doc (
|
@doc (
|
||||||
description: "query 接口"
|
description: "query 接口"
|
||||||
bizCodeEnumDescription: " 1003-用不存在<br>1004-非法操作" // 接口级别业务错误码枚举描述,会覆盖全局的业务错误码,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 时生效
|
bizCodeEnumDescription: " 1003-用不存在<br>1004-非法操作" // 接口级别业务错误码枚举描述,会覆盖全局的业务错误码,json 格式,key 为业务错误码,value 为该错误码的描述,仅当 wrapCodeMsg 为 true 且 useDefinitions 为 false 时生效
|
||||||
)
|
)
|
||||||
@handler query
|
@handler query
|
||||||
get /query (QueryReq) returns (QueryResp)
|
get /query (QueryReq) returns (QueryResp)
|
||||||
|
|||||||
5608
tools/goctl/api/swagger/example/example_cn.swagger.json
Normal file
5608
tools/goctl/api/swagger/example/example_cn.swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -81,21 +81,21 @@ func enumsValueFromOptions(options []string) []any {
|
|||||||
return []any{}
|
return []any{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func defValueFromOptions(options []string, apiType spec.Type) any {
|
func defValueFromOptions(ctx Context, options []string, apiType spec.Type) any {
|
||||||
tp := sampleTypeFromGoType(apiType)
|
tp := sampleTypeFromGoType(ctx, apiType)
|
||||||
return valueFromOptions(options, defFlag, tp)
|
return valueFromOptions(ctx, options, defFlag, tp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func exampleValueFromOptions(options []string, apiType spec.Type) any {
|
func exampleValueFromOptions(ctx Context, options []string, apiType spec.Type) any {
|
||||||
tp := sampleTypeFromGoType(apiType)
|
tp := sampleTypeFromGoType(ctx, apiType)
|
||||||
val := valueFromOptions(options, exampleFlag, tp)
|
val := valueFromOptions(ctx, options, exampleFlag, tp)
|
||||||
if val != nil {
|
if val != nil {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
return defValueFromOptions(options, apiType)
|
return defValueFromOptions(ctx, options, apiType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func valueFromOptions(options []string, key string, tp string) any {
|
func valueFromOptions(_ Context, options []string, key string, tp string) any {
|
||||||
if len(options) == 0 {
|
if len(options) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -103,16 +103,18 @@ func valueFromOptions(options []string, key string, tp string) any {
|
|||||||
if strings.HasPrefix(option, key) {
|
if strings.HasPrefix(option, key) {
|
||||||
s := option[len(key):]
|
s := option[len(key):]
|
||||||
switch tp {
|
switch tp {
|
||||||
case "integer":
|
case swaggerTypeInteger:
|
||||||
val, _ := strconv.ParseInt(s, 10, 64)
|
val, _ := strconv.ParseInt(s, 10, 64)
|
||||||
return val
|
return val
|
||||||
case "boolean":
|
case swaggerTypeBoolean:
|
||||||
val, _ := strconv.ParseBool(s)
|
val, _ := strconv.ParseBool(s)
|
||||||
return val
|
return val
|
||||||
case "number":
|
case swaggerTypeNumber:
|
||||||
val, _ := strconv.ParseFloat(s, 64)
|
val, _ := strconv.ParseFloat(s, 64)
|
||||||
return val
|
return val
|
||||||
case "string":
|
case swaggerTypeArray:
|
||||||
|
return s
|
||||||
|
case swaggerTypeString:
|
||||||
return s
|
return s
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ func TestDefValueFromOptions(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := defValueFromOptions(tt.options, tt.apiType)
|
result := defValueFromOptions(testingContext(t), tt.options, tt.apiType)
|
||||||
assert.Equal(t, tt.expected, result)
|
assert.Equal(t, tt.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -202,7 +202,7 @@ func TestExampleValueFromOptions(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
exampleValueFromOptions(tt.options, tt.apiType)
|
exampleValueFromOptions(testingContext(t), tt.options, tt.apiType)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -247,7 +247,7 @@ func TestValueFromOptions(t *testing.T) {
|
|||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
result := valueFromOptions(tt.options, tt.key, tt.tp)
|
result := valueFromOptions(testingContext(t), tt.options, tt.key, tt.tp)
|
||||||
assert.Equal(t, tt.expected, result)
|
assert.Equal(t, tt.expected, result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,25 @@ import (
|
|||||||
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
func isPostJson(ctx Context, method string, tp apiSpec.Type) (string, bool) {
|
||||||
|
if strings.EqualFold(method, http.MethodPost) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
structType, ok := tp.(apiSpec.DefineStruct)
|
||||||
|
if !ok {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
var isPostJson bool
|
||||||
|
rangeMemberAndDo(ctx, structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
|
||||||
|
jsonTag, _ := tag.Get(tagJson)
|
||||||
|
if !isPostJson {
|
||||||
|
isPostJson = jsonTag != nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return structType.RawName, isPostJson
|
||||||
|
}
|
||||||
|
|
||||||
|
func parametersFromType(ctx Context, method string, tp apiSpec.Type) []spec.Parameter {
|
||||||
if tp == nil {
|
if tp == nil {
|
||||||
return []spec.Parameter{}
|
return []spec.Parameter{}
|
||||||
}
|
}
|
||||||
@@ -16,12 +34,30 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return []spec.Parameter{}
|
return []spec.Parameter{}
|
||||||
}
|
}
|
||||||
|
structName, ok := isPostJson(ctx, method, tp)
|
||||||
|
if ok {
|
||||||
|
return []spec.Parameter{
|
||||||
|
{
|
||||||
|
ParamProps: spec.ParamProps{
|
||||||
|
In: paramsInBody,
|
||||||
|
Name: paramsInBody,
|
||||||
|
Required: true,
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: spec.SchemaProps{
|
||||||
|
Ref: spec.MustCreateRef(getRefName(structName)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
resp []spec.Parameter
|
resp []spec.Parameter
|
||||||
properties = map[string]spec.Schema{}
|
properties = map[string]spec.Schema{}
|
||||||
requiredFields []string
|
requiredFields []string
|
||||||
)
|
)
|
||||||
rangeMemberAndDo(structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
|
rangeMemberAndDo(ctx, structType, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
|
||||||
headerTag, _ := tag.Get(tagHeader)
|
headerTag, _ := tag.Get(tagHeader)
|
||||||
hasHeader := headerTag != nil
|
hasHeader := headerTag != nil
|
||||||
|
|
||||||
@@ -44,10 +80,9 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
Enum: enumsValueFromOptions(headerTag.Options),
|
Enum: enumsValueFromOptions(headerTag.Options),
|
||||||
},
|
},
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(member.Type),
|
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||||
Default: defValueFromOptions(headerTag.Options, member.Type),
|
Default: defValueFromOptions(ctx, headerTag.Options, member.Type),
|
||||||
Example: exampleValueFromOptions(headerTag.Options, member.Type),
|
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||||
Items: sampleItemsFromGoType(member.Type),
|
|
||||||
},
|
},
|
||||||
ParamProps: spec.ParamProps{
|
ParamProps: spec.ParamProps{
|
||||||
In: paramsInHeader,
|
In: paramsInHeader,
|
||||||
@@ -68,10 +103,9 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
Enum: enumsValueFromOptions(pathParameterTag.Options),
|
Enum: enumsValueFromOptions(pathParameterTag.Options),
|
||||||
},
|
},
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(member.Type),
|
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||||
Default: defValueFromOptions(pathParameterTag.Options, member.Type),
|
Default: defValueFromOptions(ctx, pathParameterTag.Options, member.Type),
|
||||||
Example: exampleValueFromOptions(pathParameterTag.Options, member.Type),
|
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||||
Items: sampleItemsFromGoType(member.Type),
|
|
||||||
},
|
},
|
||||||
ParamProps: spec.ParamProps{
|
ParamProps: spec.ParamProps{
|
||||||
In: paramsInPath,
|
In: paramsInPath,
|
||||||
@@ -93,10 +127,9 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
Enum: enumsValueFromOptions(formTag.Options),
|
Enum: enumsValueFromOptions(formTag.Options),
|
||||||
},
|
},
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(member.Type),
|
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||||
Default: defValueFromOptions(formTag.Options, member.Type),
|
Default: defValueFromOptions(ctx, formTag.Options, member.Type),
|
||||||
Example: exampleValueFromOptions(formTag.Options, member.Type),
|
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||||
Items: sampleItemsFromGoType(member.Type),
|
|
||||||
},
|
},
|
||||||
ParamProps: spec.ParamProps{
|
ParamProps: spec.ParamProps{
|
||||||
In: paramsInQuery,
|
In: paramsInQuery,
|
||||||
@@ -116,10 +149,9 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
Enum: enumsValueFromOptions(formTag.Options),
|
Enum: enumsValueFromOptions(formTag.Options),
|
||||||
},
|
},
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(member.Type),
|
Type: sampleTypeFromGoType(ctx, member.Type),
|
||||||
Default: defValueFromOptions(formTag.Options, member.Type),
|
Default: defValueFromOptions(ctx, formTag.Options, member.Type),
|
||||||
Example: exampleValueFromOptions(formTag.Options, member.Type),
|
Items: sampleItemsFromGoType(ctx, member.Type),
|
||||||
Items: sampleItemsFromGoType(member.Type),
|
|
||||||
},
|
},
|
||||||
ParamProps: spec.ParamProps{
|
ParamProps: spec.ParamProps{
|
||||||
In: paramsInForm,
|
In: paramsInForm,
|
||||||
@@ -139,25 +171,25 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
}
|
}
|
||||||
var schema = spec.Schema{
|
var schema = spec.Schema{
|
||||||
SwaggerSchemaProps: spec.SwaggerSchemaProps{
|
SwaggerSchemaProps: spec.SwaggerSchemaProps{
|
||||||
Example: exampleValueFromOptions(jsonTag.Options, member.Type),
|
Example: exampleValueFromOptions(ctx, jsonTag.Options, member.Type),
|
||||||
},
|
},
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: formatComment(member.Comment),
|
Description: formatComment(member.Comment),
|
||||||
Type: typeFromGoType(member.Type),
|
Type: typeFromGoType(ctx, member.Type),
|
||||||
Default: defValueFromOptions(jsonTag.Options, member.Type),
|
Default: defValueFromOptions(ctx, jsonTag.Options, member.Type),
|
||||||
Maximum: maximum,
|
Maximum: maximum,
|
||||||
ExclusiveMaximum: exclusiveMaximum,
|
ExclusiveMaximum: exclusiveMaximum,
|
||||||
Minimum: minimum,
|
Minimum: minimum,
|
||||||
ExclusiveMinimum: exclusiveMinimum,
|
ExclusiveMinimum: exclusiveMinimum,
|
||||||
Enum: enumsValueFromOptions(jsonTag.Options),
|
Enum: enumsValueFromOptions(jsonTag.Options),
|
||||||
AdditionalProperties: mapFromGoType(member.Type),
|
AdditionalProperties: mapFromGoType(ctx, member.Type),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch sampleTypeFromGoType(member.Type) {
|
switch sampleTypeFromGoType(ctx, member.Type) {
|
||||||
case swaggerTypeArray:
|
case swaggerTypeArray:
|
||||||
schema.Items = itemsFromGoType(member.Type)
|
schema.Items = itemsFromGoType(ctx, member.Type)
|
||||||
case swaggerTypeObject:
|
case swaggerTypeObject:
|
||||||
p, r := propertiesFromType(member.Type)
|
p, r := propertiesFromType(ctx, member.Type)
|
||||||
schema.Properties = p
|
schema.Properties = p
|
||||||
schema.Required = r
|
schema.Required = r
|
||||||
}
|
}
|
||||||
@@ -172,7 +204,7 @@ func parametersFromType(method string, tp apiSpec.Type) []spec.Parameter {
|
|||||||
Required: true,
|
Required: true,
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Type: typeFromGoType(structType),
|
Type: typeFromGoType(ctx, structType),
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
Required: requiredFields,
|
Required: requiredFields,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,18 +9,18 @@ import (
|
|||||||
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func spec2Paths(info apiSpec.Info, srv apiSpec.Service) *spec.Paths {
|
func spec2Paths(ctx Context, srv apiSpec.Service) *spec.Paths {
|
||||||
paths := &spec.Paths{
|
paths := &spec.Paths{
|
||||||
Paths: make(map[string]spec.PathItem),
|
Paths: make(map[string]spec.PathItem),
|
||||||
}
|
}
|
||||||
for _, group := range srv.Groups {
|
for _, group := range srv.Groups {
|
||||||
prefix := path.Clean(strings.TrimPrefix(group.GetAnnotation("prefix"), "/"))
|
prefix := path.Clean(strings.TrimPrefix(group.GetAnnotation(propertyKeyPrefix), "/"))
|
||||||
for _, route := range group.Routes {
|
for _, route := range group.Routes {
|
||||||
routPath := pathVariable2SwaggerVariable(route.Path)
|
routPath := pathVariable2SwaggerVariable(ctx, route.Path)
|
||||||
if len(prefix) > 0 && prefix != "." {
|
if len(prefix) > 0 && prefix != "." {
|
||||||
routPath = "/" + path.Clean(prefix) + routPath
|
routPath = "/" + path.Clean(prefix) + routPath
|
||||||
}
|
}
|
||||||
pathItem := spec2Path(info, group, route)
|
pathItem := spec2Path(ctx, group, route)
|
||||||
existPathItem, ok := paths.Paths[routPath]
|
existPathItem, ok := paths.Paths[routPath]
|
||||||
if !ok {
|
if !ok {
|
||||||
paths.Paths[routPath] = pathItem
|
paths.Paths[routPath] = pathItem
|
||||||
@@ -60,8 +60,8 @@ func mergePathItem(old, new spec.PathItem) spec.PathItem {
|
|||||||
return old
|
return old
|
||||||
}
|
}
|
||||||
|
|
||||||
func spec2Path(info apiSpec.Info, group apiSpec.Group, route apiSpec.Route) spec.PathItem {
|
func spec2Path(ctx Context, group apiSpec.Group, route apiSpec.Route) spec.PathItem {
|
||||||
authType := getStringFromKVOrDefault(group.Annotation.Properties, "authType", "")
|
authType := getStringFromKVOrDefault(group.Annotation.Properties, propertyKeyAuthType, "")
|
||||||
var security []map[string][]string
|
var security []map[string][]string
|
||||||
if len(authType) > 0 {
|
if len(authType) > 0 {
|
||||||
security = []map[string][]string{
|
security = []map[string][]string{
|
||||||
@@ -72,20 +72,20 @@ func spec2Path(info apiSpec.Info, group apiSpec.Group, route apiSpec.Route) spec
|
|||||||
}
|
}
|
||||||
op := &spec.Operation{
|
op := &spec.Operation{
|
||||||
OperationProps: spec.OperationProps{
|
OperationProps: spec.OperationProps{
|
||||||
Description: getStringFromKVOrDefault(route.AtDoc.Properties, "description", ""),
|
Description: getStringFromKVOrDefault(route.AtDoc.Properties, propertyKeyDescription, ""),
|
||||||
Consumes: consumesFromTypeOrDef(route.Method, route.RequestType),
|
Consumes: consumesFromTypeOrDef(ctx, route.Method, route.RequestType),
|
||||||
Produces: getListFromInfoOrDefault(route.AtDoc.Properties, "produces", []string{applicationJson}),
|
Produces: getListFromInfoOrDefault(route.AtDoc.Properties, propertyKeyProduces, []string{applicationJson}),
|
||||||
Schemes: getListFromInfoOrDefault(route.AtDoc.Properties, "schemes", []string{schemeHttps}),
|
Schemes: getListFromInfoOrDefault(route.AtDoc.Properties, propertyKeySchemes, []string{schemeHttps}),
|
||||||
Tags: getListFromInfoOrDefault(group.Annotation.Properties, "tags", []string{""}),
|
Tags: getListFromInfoOrDefault(group.Annotation.Properties, propertyKeyTags, getListFromInfoOrDefault(group.Annotation.Properties, propertyKeySummary, []string{})),
|
||||||
Summary: getStringFromKVOrDefault(route.AtDoc.Properties, "summary", getFirstUsableString(route.AtDoc.Text, route.Handler)),
|
Summary: getStringFromKVOrDefault(route.AtDoc.Properties, propertyKeySummary, getFirstUsableString(route.AtDoc.Text, route.Handler)),
|
||||||
Deprecated: getBoolFromKVOrDefault(route.AtDoc.Properties, "deprecated", false),
|
Deprecated: getBoolFromKVOrDefault(route.AtDoc.Properties, propertyKeyDeprecated, false),
|
||||||
Parameters: parametersFromType(route.Method, route.RequestType),
|
Parameters: parametersFromType(ctx, route.Method, route.RequestType),
|
||||||
Responses: jsonResponseFromType(info, route.AtDoc, route.ResponseType),
|
Responses: jsonResponseFromType(ctx, route.AtDoc, route.ResponseType),
|
||||||
Security: security,
|
Security: security,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
externalDocsDescription := getStringFromKVOrDefault(route.AtDoc.Properties, "externalDocsDescription", "")
|
externalDocsDescription := getStringFromKVOrDefault(route.AtDoc.Properties, propertyKeyExternalDocsDescription, "")
|
||||||
externalDocsURL := getStringFromKVOrDefault(route.AtDoc.Properties, "externalDocsURL", "")
|
externalDocsURL := getStringFromKVOrDefault(route.AtDoc.Properties, propertyKeyExternalDocsURL, "")
|
||||||
if len(externalDocsDescription) > 0 || len(externalDocsURL) > 0 {
|
if len(externalDocsDescription) > 0 || len(externalDocsURL) > 0 {
|
||||||
op.ExternalDocs = &spec.ExternalDocumentation{
|
op.ExternalDocs = &spec.ExternalDocumentation{
|
||||||
Description: externalDocsDescription,
|
Description: externalDocsDescription,
|
||||||
|
|||||||
@@ -5,18 +5,18 @@ import (
|
|||||||
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func propertiesFromType(tp apiSpec.Type) (spec.SchemaProperties, []string) {
|
func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, []string) {
|
||||||
var (
|
var (
|
||||||
properties = map[string]spec.Schema{}
|
properties = map[string]spec.Schema{}
|
||||||
requiredFields []string
|
requiredFields []string
|
||||||
)
|
)
|
||||||
switch val := tp.(type) {
|
switch val := tp.(type) {
|
||||||
case apiSpec.PointerType:
|
case apiSpec.PointerType:
|
||||||
return propertiesFromType(val.Type)
|
return propertiesFromType(ctx, val.Type)
|
||||||
case apiSpec.ArrayType:
|
case apiSpec.ArrayType:
|
||||||
return propertiesFromType(val.Value)
|
return propertiesFromType(ctx, val.Value)
|
||||||
case apiSpec.DefineStruct, apiSpec.NestedStruct:
|
case apiSpec.DefineStruct, apiSpec.NestedStruct:
|
||||||
rangeMemberAndDo(val, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
|
rangeMemberAndDo(ctx, val, func(tag *apiSpec.Tags, required bool, member apiSpec.Member) {
|
||||||
var (
|
var (
|
||||||
jsonTagString = member.Name
|
jsonTagString = member.Name
|
||||||
minimum, maximum *float64
|
minimum, maximum *float64
|
||||||
@@ -28,38 +28,46 @@ func propertiesFromType(tp apiSpec.Type) (spec.SchemaProperties, []string) {
|
|||||||
if jsonTag != nil {
|
if jsonTag != nil {
|
||||||
jsonTagString = jsonTag.Name
|
jsonTagString = jsonTag.Name
|
||||||
minimum, maximum, exclusiveMinimum, exclusiveMaximum = rangeValueFromOptions(jsonTag.Options)
|
minimum, maximum, exclusiveMinimum, exclusiveMaximum = rangeValueFromOptions(jsonTag.Options)
|
||||||
example = exampleValueFromOptions(jsonTag.Options, member.Type)
|
example = exampleValueFromOptions(ctx, jsonTag.Options, member.Type)
|
||||||
defaultValue = defValueFromOptions(jsonTag.Options, member.Type)
|
defaultValue = defValueFromOptions(ctx, jsonTag.Options, member.Type)
|
||||||
enum = enumsValueFromOptions(jsonTag.Options)
|
enum = enumsValueFromOptions(jsonTag.Options)
|
||||||
}
|
}
|
||||||
|
|
||||||
if required {
|
if required {
|
||||||
requiredFields = append(requiredFields, jsonTagString)
|
requiredFields = append(requiredFields, jsonTagString)
|
||||||
}
|
}
|
||||||
var schema = spec.Schema{
|
|
||||||
|
schema := spec.Schema{
|
||||||
SwaggerSchemaProps: spec.SwaggerSchemaProps{
|
SwaggerSchemaProps: spec.SwaggerSchemaProps{
|
||||||
Example: example,
|
Example: example,
|
||||||
},
|
},
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Description: formatComment(member.Comment),
|
Description: formatComment(member.Comment),
|
||||||
Type: typeFromGoType(member.Type),
|
Type: typeFromGoType(ctx, member.Type),
|
||||||
Default: defaultValue,
|
Default: defaultValue,
|
||||||
Maximum: maximum,
|
Maximum: maximum,
|
||||||
ExclusiveMaximum: exclusiveMaximum,
|
ExclusiveMaximum: exclusiveMaximum,
|
||||||
Minimum: minimum,
|
Minimum: minimum,
|
||||||
ExclusiveMinimum: exclusiveMinimum,
|
ExclusiveMinimum: exclusiveMinimum,
|
||||||
Enum: enum,
|
Enum: enum,
|
||||||
AdditionalProperties: mapFromGoType(member.Type),
|
AdditionalProperties: mapFromGoType(ctx, member.Type),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch sampleTypeFromGoType(member.Type) {
|
|
||||||
|
switch sampleTypeFromGoType(ctx, member.Type) {
|
||||||
case swaggerTypeArray:
|
case swaggerTypeArray:
|
||||||
schema.Items = itemsFromGoType(member.Type)
|
schema.Items = itemsFromGoType(ctx, member.Type)
|
||||||
case swaggerTypeObject:
|
case swaggerTypeObject:
|
||||||
p, r := propertiesFromType(member.Type)
|
p, r := propertiesFromType(ctx, member.Type)
|
||||||
schema.Properties = p
|
schema.Properties = p
|
||||||
schema.Required = r
|
schema.Required = r
|
||||||
}
|
}
|
||||||
|
if ctx.UseDefinitions {
|
||||||
|
structName, containsStruct := containsStruct(member.Type)
|
||||||
|
if containsStruct {
|
||||||
|
schema.SchemaProps.Ref = spec.MustCreateRef(getRefName(structName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
properties[jsonTagString] = schema
|
properties[jsonTagString] = schema
|
||||||
})
|
})
|
||||||
@@ -67,3 +75,22 @@ func propertiesFromType(tp apiSpec.Type) (spec.SchemaProperties, []string) {
|
|||||||
|
|
||||||
return properties, requiredFields
|
return properties, requiredFields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containsStruct(tp apiSpec.Type) (string, bool) {
|
||||||
|
switch val := tp.(type) {
|
||||||
|
case apiSpec.PointerType:
|
||||||
|
return containsStruct(val.Type)
|
||||||
|
case apiSpec.ArrayType:
|
||||||
|
return containsStruct(val.Value)
|
||||||
|
case apiSpec.DefineStruct:
|
||||||
|
return val.RawName, true
|
||||||
|
case apiSpec.MapType:
|
||||||
|
return containsStruct(val.Value)
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getRefName(typeName string) string {
|
||||||
|
return "#/definitions/" + typeName
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,25 +1,62 @@
|
|||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
)
|
)
|
||||||
|
|
||||||
func jsonResponseFromType(info apiSpec.Info, atDoc apiSpec.AtDoc, tp apiSpec.Type) *spec.Responses {
|
func jsonResponseFromType(ctx Context, atDoc apiSpec.AtDoc, tp apiSpec.Type) *spec.Responses {
|
||||||
p, _ := propertiesFromType(tp)
|
if tp == nil {
|
||||||
|
return &spec.Responses{
|
||||||
|
ResponsesProps: spec.ResponsesProps{
|
||||||
|
StatusCodeResponses: map[int]spec.Response{
|
||||||
|
http.StatusOK: {
|
||||||
|
ResponseProps: spec.ResponseProps{
|
||||||
|
Description: "",
|
||||||
|
Schema: &spec.Schema{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
props := spec.SchemaProps{
|
props := spec.SchemaProps{
|
||||||
Type: typeFromGoType(tp),
|
AdditionalProperties: mapFromGoType(ctx, tp),
|
||||||
Properties: p,
|
Items: itemsFromGoType(ctx, tp),
|
||||||
AdditionalProperties: mapFromGoType(tp),
|
}
|
||||||
Items: itemsFromGoType(tp),
|
if ctx.UseDefinitions {
|
||||||
|
structName, ok := containsStruct(tp)
|
||||||
|
if ok {
|
||||||
|
props.Ref = spec.MustCreateRef(getRefName(structName))
|
||||||
|
return &spec.Responses{
|
||||||
|
ResponsesProps: spec.ResponsesProps{
|
||||||
|
StatusCodeResponses: map[int]spec.Response{
|
||||||
|
http.StatusOK: {
|
||||||
|
ResponseProps: spec.ResponseProps{
|
||||||
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p, _ := propertiesFromType(ctx, tp)
|
||||||
|
props.Type = typeFromGoType(ctx, tp)
|
||||||
|
props.Properties = p
|
||||||
return &spec.Responses{
|
return &spec.Responses{
|
||||||
ResponsesProps: spec.ResponsesProps{
|
ResponsesProps: spec.ResponsesProps{
|
||||||
Default: &spec.Response{
|
StatusCodeResponses: map[int]spec.Response{
|
||||||
ResponseProps: spec.ResponseProps{
|
http.StatusOK: {
|
||||||
Schema: &spec.Schema{
|
ResponseProps: spec.ResponseProps{
|
||||||
SchemaProps: wrapCodeMsgProps(props, info, atDoc),
|
Schema: &spec.Schema{
|
||||||
|
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,12 +8,11 @@ import (
|
|||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
apiSpec "github.com/zeromicro/go-zero/tools/goctl/api/spec"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/internal/version"
|
"github.com/zeromicro/go-zero/tools/goctl/internal/version"
|
||||||
"github.com/zeromicro/go-zero/tools/goctl/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func spec2Swagger(api *apiSpec.ApiSpec) (*spec.Swagger, error) {
|
func spec2Swagger(api *apiSpec.ApiSpec) (*spec.Swagger, error) {
|
||||||
|
ctx := contextFromApi(api.Info)
|
||||||
extensions, info := specExtensions(api.Info)
|
extensions, info := specExtensions(api.Info)
|
||||||
|
|
||||||
var securityDefinitions spec.SecurityDefinitions
|
var securityDefinitions spec.SecurityDefinitions
|
||||||
securityDefinitionsFromJson := getStringFromKVOrDefault(api.Info.Properties, "securityDefinitionsFromJson", `{}`)
|
securityDefinitionsFromJson := getStringFromKVOrDefault(api.Info.Properties, "securityDefinitionsFromJson", `{}`)
|
||||||
_ = json.Unmarshal([]byte(securityDefinitionsFromJson), &securityDefinitions)
|
_ = json.Unmarshal([]byte(securityDefinitionsFromJson), &securityDefinitions)
|
||||||
@@ -22,14 +21,15 @@ func spec2Swagger(api *apiSpec.ApiSpec) (*spec.Swagger, error) {
|
|||||||
Extensions: extensions,
|
Extensions: extensions,
|
||||||
},
|
},
|
||||||
SwaggerProps: spec.SwaggerProps{
|
SwaggerProps: spec.SwaggerProps{
|
||||||
Consumes: getListFromInfoOrDefault(api.Info.Properties, "consumes", []string{applicationJson}),
|
Definitions: definitionsFromTypes(ctx, api.Types),
|
||||||
Produces: getListFromInfoOrDefault(api.Info.Properties, "produces", []string{applicationJson}),
|
Consumes: getListFromInfoOrDefault(api.Info.Properties, propertyKeyConsumes, []string{applicationJson}),
|
||||||
Schemes: getListFromInfoOrDefault(api.Info.Properties, "schemes", []string{schemeHttps}),
|
Produces: getListFromInfoOrDefault(api.Info.Properties, propertyKeyProduces, []string{applicationJson}),
|
||||||
|
Schemes: getListFromInfoOrDefault(api.Info.Properties, propertyKeySchemes, []string{schemeHttps}),
|
||||||
Swagger: swaggerVersion,
|
Swagger: swaggerVersion,
|
||||||
Info: info,
|
Info: info,
|
||||||
Host: getStringFromKVOrDefault(api.Info.Properties, "host", defaultHost),
|
Host: getStringFromKVOrDefault(api.Info.Properties, propertyKeyHost, ""),
|
||||||
BasePath: getStringFromKVOrDefault(api.Info.Properties, "basePath", defaultBasePath),
|
BasePath: getStringFromKVOrDefault(api.Info.Properties, propertyKeyBasePath, defaultBasePath),
|
||||||
Paths: spec2Paths(api.Info, api.Service),
|
Paths: spec2Paths(ctx, api.Service),
|
||||||
SecurityDefinitions: securityDefinitions,
|
SecurityDefinitions: securityDefinitions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ func formatComment(comment string) string {
|
|||||||
return strings.TrimSpace(s)
|
return strings.TrimSpace(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sampleItemsFromGoType(tp apiSpec.Type) *spec.Items {
|
func sampleItemsFromGoType(ctx Context, tp apiSpec.Type) *spec.Items {
|
||||||
val, ok := tp.(apiSpec.ArrayType)
|
val, ok := tp.(apiSpec.ArrayType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@@ -52,14 +52,14 @@ func sampleItemsFromGoType(tp apiSpec.Type) *spec.Items {
|
|||||||
case apiSpec.PrimitiveType:
|
case apiSpec.PrimitiveType:
|
||||||
return &spec.Items{
|
return &spec.Items{
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(item),
|
Type: sampleTypeFromGoType(ctx, item),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case apiSpec.ArrayType:
|
case apiSpec.ArrayType:
|
||||||
return &spec.Items{
|
return &spec.Items{
|
||||||
SimpleSchema: spec.SimpleSchema{
|
SimpleSchema: spec.SimpleSchema{
|
||||||
Type: sampleTypeFromGoType(item),
|
Type: sampleTypeFromGoType(ctx, item),
|
||||||
Items: sampleItemsFromGoType(item),
|
Items: sampleItemsFromGoType(ctx, item),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
default: // unsupported type
|
default: // unsupported type
|
||||||
@@ -68,30 +68,30 @@ func sampleItemsFromGoType(tp apiSpec.Type) *spec.Items {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// itemsFromGoType returns the schema or array of the type, just for non json body parameters.
|
// itemsFromGoType returns the schema or array of the type, just for non json body parameters.
|
||||||
func itemsFromGoType(tp apiSpec.Type) *spec.SchemaOrArray {
|
func itemsFromGoType(ctx Context, tp apiSpec.Type) *spec.SchemaOrArray {
|
||||||
array, ok := tp.(apiSpec.ArrayType)
|
array, ok := tp.(apiSpec.ArrayType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return itemFromGoType(array.Value)
|
return itemFromGoType(ctx, array.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapFromGoType(tp apiSpec.Type) *spec.SchemaOrBool {
|
func mapFromGoType(ctx Context, tp apiSpec.Type) *spec.SchemaOrBool {
|
||||||
mapType, ok := tp.(apiSpec.MapType)
|
mapType, ok := tp.(apiSpec.MapType)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var schema = &spec.Schema{
|
var schema = &spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Type: typeFromGoType(mapType.Value),
|
Type: typeFromGoType(ctx, mapType.Value),
|
||||||
AdditionalProperties: mapFromGoType(mapType.Value),
|
AdditionalProperties: mapFromGoType(ctx, mapType.Value),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
switch sampleTypeFromGoType(mapType.Value) {
|
switch sampleTypeFromGoType(ctx, mapType.Value) {
|
||||||
case swaggerTypeArray:
|
case swaggerTypeArray:
|
||||||
schema.Items = itemsFromGoType(mapType.Value)
|
schema.Items = itemsFromGoType(ctx, mapType.Value)
|
||||||
case swaggerTypeObject:
|
case swaggerTypeObject:
|
||||||
p, r := propertiesFromType(mapType.Value)
|
p, r := propertiesFromType(ctx, mapType.Value)
|
||||||
schema.Properties = p
|
schema.Properties = p
|
||||||
schema.Required = r
|
schema.Required = r
|
||||||
}
|
}
|
||||||
@@ -102,37 +102,37 @@ func mapFromGoType(tp apiSpec.Type) *spec.SchemaOrBool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// itemFromGoType returns the schema or array of the type, just for non json body parameters.
|
// itemFromGoType returns the schema or array of the type, just for non json body parameters.
|
||||||
func itemFromGoType(tp apiSpec.Type) *spec.SchemaOrArray {
|
func itemFromGoType(ctx Context, tp apiSpec.Type) *spec.SchemaOrArray {
|
||||||
switch itemType := tp.(type) {
|
switch itemType := tp.(type) {
|
||||||
case apiSpec.PrimitiveType:
|
case apiSpec.PrimitiveType:
|
||||||
return &spec.SchemaOrArray{
|
return &spec.SchemaOrArray{
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Type: typeFromGoType(tp),
|
Type: typeFromGoType(ctx, tp),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case apiSpec.DefineStruct, apiSpec.NestedStruct:
|
case apiSpec.DefineStruct, apiSpec.NestedStruct, apiSpec.MapType:
|
||||||
properties, requiredFields := propertiesFromType(itemType)
|
properties, requiredFields := propertiesFromType(ctx, itemType)
|
||||||
return &spec.SchemaOrArray{
|
return &spec.SchemaOrArray{
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Type: typeFromGoType(itemType),
|
Type: typeFromGoType(ctx, itemType),
|
||||||
Items: itemsFromGoType(itemType),
|
Items: itemsFromGoType(ctx, itemType),
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
Required: requiredFields,
|
Required: requiredFields,
|
||||||
AdditionalProperties: mapFromGoType(itemType),
|
AdditionalProperties: mapFromGoType(ctx, itemType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
case apiSpec.PointerType:
|
case apiSpec.PointerType:
|
||||||
return itemFromGoType(itemType.Type)
|
return itemFromGoType(ctx, itemType.Type)
|
||||||
case apiSpec.ArrayType:
|
case apiSpec.ArrayType:
|
||||||
return &spec.SchemaOrArray{
|
return &spec.SchemaOrArray{
|
||||||
Schema: &spec.Schema{
|
Schema: &spec.Schema{
|
||||||
SchemaProps: spec.SchemaProps{
|
SchemaProps: spec.SchemaProps{
|
||||||
Type: typeFromGoType(itemType),
|
Type: typeFromGoType(ctx, itemType),
|
||||||
Items: itemsFromGoType(itemType),
|
Items: itemsFromGoType(ctx, itemType),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ func itemFromGoType(tp apiSpec.Type) *spec.SchemaOrArray {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func typeFromGoType(tp apiSpec.Type) []string {
|
func typeFromGoType(ctx Context, tp apiSpec.Type) []string {
|
||||||
switch val := tp.(type) {
|
switch val := tp.(type) {
|
||||||
case apiSpec.PrimitiveType:
|
case apiSpec.PrimitiveType:
|
||||||
res, ok := tpMapper[val.RawName]
|
res, ok := tpMapper[val.RawName]
|
||||||
@@ -152,12 +152,12 @@ func typeFromGoType(tp apiSpec.Type) []string {
|
|||||||
case apiSpec.DefineStruct, apiSpec.MapType:
|
case apiSpec.DefineStruct, apiSpec.MapType:
|
||||||
return []string{swaggerTypeObject}
|
return []string{swaggerTypeObject}
|
||||||
case apiSpec.PointerType:
|
case apiSpec.PointerType:
|
||||||
return typeFromGoType(val.Type)
|
return typeFromGoType(ctx, val.Type)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sampleTypeFromGoType(tp apiSpec.Type) string {
|
func sampleTypeFromGoType(ctx Context, tp apiSpec.Type) string {
|
||||||
switch val := tp.(type) {
|
switch val := tp.(type) {
|
||||||
case apiSpec.PrimitiveType:
|
case apiSpec.PrimitiveType:
|
||||||
return tpMapper[val.RawName]
|
return tpMapper[val.RawName]
|
||||||
@@ -166,13 +166,13 @@ func sampleTypeFromGoType(tp apiSpec.Type) string {
|
|||||||
case apiSpec.DefineStruct, apiSpec.MapType, apiSpec.NestedStruct:
|
case apiSpec.DefineStruct, apiSpec.MapType, apiSpec.NestedStruct:
|
||||||
return swaggerTypeObject
|
return swaggerTypeObject
|
||||||
case apiSpec.PointerType:
|
case apiSpec.PointerType:
|
||||||
return sampleTypeFromGoType(val.Type)
|
return sampleTypeFromGoType(ctx, val.Type)
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func typeContainsTag(structType apiSpec.DefineStruct, tag string) bool {
|
func typeContainsTag(_ Context, structType apiSpec.DefineStruct, tag string) bool {
|
||||||
for _, field := range structType.Members {
|
for _, field := range structType.Members {
|
||||||
tags, _ := apiSpec.Parse(field.Tag)
|
tags, _ := apiSpec.Parse(field.Tag)
|
||||||
for _, t := range tags.Tags() {
|
for _, t := range tags.Tags() {
|
||||||
@@ -184,13 +184,13 @@ func typeContainsTag(structType apiSpec.DefineStruct, tag string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandMembers(tp apiSpec.Type) []apiSpec.Member {
|
func expandMembers(ctx Context, tp apiSpec.Type) []apiSpec.Member {
|
||||||
var members []apiSpec.Member
|
var members []apiSpec.Member
|
||||||
switch val := tp.(type) {
|
switch val := tp.(type) {
|
||||||
case apiSpec.DefineStruct:
|
case apiSpec.DefineStruct:
|
||||||
for _, v := range val.Members {
|
for _, v := range val.Members {
|
||||||
if v.IsInline {
|
if v.IsInline {
|
||||||
members = append(members, expandMembers(v.Type)...)
|
members = append(members, expandMembers(ctx, v.Type)...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
members = append(members, v)
|
members = append(members, v)
|
||||||
@@ -198,7 +198,7 @@ func expandMembers(tp apiSpec.Type) []apiSpec.Member {
|
|||||||
case apiSpec.NestedStruct:
|
case apiSpec.NestedStruct:
|
||||||
for _, v := range val.Members {
|
for _, v := range val.Members {
|
||||||
if v.IsInline {
|
if v.IsInline {
|
||||||
members = append(members, expandMembers(v.Type)...)
|
members = append(members, expandMembers(ctx, v.Type)...)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
members = append(members, v)
|
members = append(members, v)
|
||||||
@@ -208,42 +208,42 @@ func expandMembers(tp apiSpec.Type) []apiSpec.Member {
|
|||||||
return members
|
return members
|
||||||
}
|
}
|
||||||
|
|
||||||
func rangeMemberAndDo(structType apiSpec.Type, do func(tag *apiSpec.Tags, required bool, member apiSpec.Member)) {
|
func rangeMemberAndDo(ctx Context, structType apiSpec.Type, do func(tag *apiSpec.Tags, required bool, member apiSpec.Member)) {
|
||||||
var members = expandMembers(structType)
|
var members = expandMembers(ctx, structType)
|
||||||
|
|
||||||
for _, field := range members {
|
for _, field := range members {
|
||||||
tags, _ := apiSpec.Parse(field.Tag)
|
tags, _ := apiSpec.Parse(field.Tag)
|
||||||
required := isRequired(tags)
|
required := isRequired(ctx, tags)
|
||||||
do(tags, required, field)
|
do(tags, required, field)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func isRequired(tags *apiSpec.Tags) bool {
|
func isRequired(ctx Context, tags *apiSpec.Tags) bool {
|
||||||
tag, err := tags.Get(tagJson)
|
tag, err := tags.Get(tagJson)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return !isOptional(tag.Options)
|
return !isOptional(ctx, tag.Options)
|
||||||
}
|
}
|
||||||
tag, err = tags.Get(tagForm)
|
tag, err = tags.Get(tagForm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return !isOptional(tag.Options)
|
return !isOptional(ctx, tag.Options)
|
||||||
}
|
}
|
||||||
tag, err = tags.Get(tagPath)
|
tag, err = tags.Get(tagPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return !isOptional(tag.Options)
|
return !isOptional(ctx, tag.Options)
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func isOptional(options []string) bool {
|
func isOptional(_ Context, options []string) bool {
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
if option == "optional" {
|
if option == optionalFlag {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathVariable2SwaggerVariable(path string) string {
|
func pathVariable2SwaggerVariable(_ Context, path string) string {
|
||||||
pathItems := strings.FieldsFunc(path, slashRune)
|
pathItems := strings.FieldsFunc(path, slashRune)
|
||||||
var resp []string
|
var resp []string
|
||||||
for _, v := range pathItems {
|
for _, v := range pathItems {
|
||||||
@@ -256,13 +256,12 @@ func pathVariable2SwaggerVariable(path string) string {
|
|||||||
return "/" + strings.Join(resp, "/")
|
return "/" + strings.Join(resp, "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrapCodeMsgProps(properties spec.SchemaProps, api apiSpec.Info, atDoc apiSpec.AtDoc) spec.SchemaProps {
|
func wrapCodeMsgProps(ctx Context, properties spec.SchemaProps, atDoc apiSpec.AtDoc) spec.SchemaProps {
|
||||||
wrapCodeMsg := getBoolFromKVOrDefault(api.Properties, "wrapCodeMsg", false)
|
if !ctx.WrapCodeMsg {
|
||||||
if !wrapCodeMsg {
|
|
||||||
return properties
|
return properties
|
||||||
}
|
}
|
||||||
globalCodeDesc := getStringFromKVOrDefault(api.Properties, "bizCodeEnumDescription", "business code")
|
globalCodeDesc := ctx.BizCodeEnumDescription
|
||||||
methodCodeDesc := getStringFromKVOrDefault(atDoc.Properties, "bizCodeEnumDescription", globalCodeDesc)
|
methodCodeDesc := getStringFromKVOrDefault(atDoc.Properties, propertyKeyBizCodeEnumDescription, globalCodeDesc)
|
||||||
return spec.SchemaProps{
|
return spec.SchemaProps{
|
||||||
Type: []string{swaggerTypeObject},
|
Type: []string{swaggerTypeObject},
|
||||||
Properties: spec.SchemaProperties{
|
Properties: spec.SchemaProperties{
|
||||||
@@ -300,22 +299,22 @@ func specExtensions(api apiSpec.Info) (spec.Extensions, *spec.Info) {
|
|||||||
ext.Add("x-go-zero-doc", "https://go-zero.dev/")
|
ext.Add("x-go-zero-doc", "https://go-zero.dev/")
|
||||||
|
|
||||||
info := &spec.Info{}
|
info := &spec.Info{}
|
||||||
info.Description = util.Unquote(api.Properties["description"])
|
info.Title = getStringFromKVOrDefault(api.Properties, propertyKeyTitle, "")
|
||||||
info.Title = util.Unquote(api.Properties["title"])
|
info.Description = getStringFromKVOrDefault(api.Properties, propertyKeyDescription, "")
|
||||||
info.TermsOfService = util.Unquote(api.Properties["termsOfService"])
|
info.TermsOfService = getStringFromKVOrDefault(api.Properties, propertyKeyTermsOfService, "")
|
||||||
info.Version = util.Unquote(api.Properties["version"])
|
info.Version = getStringFromKVOrDefault(api.Properties, propertyKeyVersion, "1.0")
|
||||||
|
|
||||||
contactInfo := spec.ContactInfo{}
|
contactInfo := spec.ContactInfo{}
|
||||||
contactInfo.Name = util.Unquote(api.Properties["contactName"])
|
contactInfo.Name = getStringFromKVOrDefault(api.Properties, propertyKeyContactName, "")
|
||||||
contactInfo.URL = util.Unquote(api.Properties["contactURL"])
|
contactInfo.URL = getStringFromKVOrDefault(api.Properties, propertyKeyContactURL, "")
|
||||||
contactInfo.Email = util.Unquote(api.Properties["contactEmail"])
|
contactInfo.Email = getStringFromKVOrDefault(api.Properties, propertyKeyContactEmail, "")
|
||||||
if len(contactInfo.Name) > 0 || len(contactInfo.URL) > 0 || len(contactInfo.Email) > 0 {
|
if len(contactInfo.Name) > 0 || len(contactInfo.URL) > 0 || len(contactInfo.Email) > 0 {
|
||||||
info.Contact = &contactInfo
|
info.Contact = &contactInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
license := &spec.License{}
|
license := &spec.License{}
|
||||||
license.Name = util.Unquote(api.Properties["licenseName"])
|
license.Name = getStringFromKVOrDefault(api.Properties, propertyKeyLicenseName, "")
|
||||||
license.URL = util.Unquote(api.Properties["licenseURL"])
|
license.URL = getStringFromKVOrDefault(api.Properties, propertyKeyLicenseURL, "")
|
||||||
if len(license.Name) > 0 || len(license.URL) > 0 {
|
if len(license.Name) > 0 || len(license.URL) > 0 {
|
||||||
info.License = license
|
info.License = license
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func Test_pathVariable2SwaggerVariable(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
result := pathVariable2SwaggerVariable(tc.input)
|
result := pathVariable2SwaggerVariable(testingContext(t), tc.input)
|
||||||
assert.Equal(t, tc.expected, result)
|
assert.Equal(t, tc.expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
tools/goctl/build.env
Normal file
2
tools/goctl/build.env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
APP_NAME=goctl
|
||||||
|
APP_VERSION=1.8.4-alpha
|
||||||
50
tools/goctl/build.sh
Normal file
50
tools/goctl/build.sh
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source build.env
|
||||||
|
APP_NAME=$APP_NAME
|
||||||
|
VERSION=$APP_VERSION
|
||||||
|
BUILD_DIR="dist"
|
||||||
|
ZIP_DIR="${BUILD_DIR}/zips"
|
||||||
|
|
||||||
|
PLATFORMS=(
|
||||||
|
"linux/amd64"
|
||||||
|
"linux/arm64"
|
||||||
|
"darwin/amd64"
|
||||||
|
"darwin/arm64"
|
||||||
|
"windows/amd64"
|
||||||
|
"windows/arm64"
|
||||||
|
)
|
||||||
|
|
||||||
|
rm -rf "${BUILD_DIR}"
|
||||||
|
mkdir -p "${ZIP_DIR}"
|
||||||
|
|
||||||
|
for PLATFORM in "${PLATFORMS[@]}"; do
|
||||||
|
GOOS=${PLATFORM%/*}
|
||||||
|
GOARCH=${PLATFORM#*/}
|
||||||
|
|
||||||
|
OUTPUT="${BUILD_DIR}/${APP_NAME}-${VERSION}-${GOOS}-${GOARCH}"
|
||||||
|
|
||||||
|
if [ "${GOOS}" = "windows" ]; then
|
||||||
|
OUTPUT="${OUTPUT}.exe"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Building for ${GOOS}/${GOARCH}..."
|
||||||
|
|
||||||
|
env GOOS="${GOOS}" GOARCH="${GOARCH}" go build -o "${OUTPUT}" goctl.go
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Error building for ${GOOS}/${GOARCH}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ZIP_OUTPUT="${ZIP_DIR}/$(basename "${OUTPUT}")"
|
||||||
|
if [ "${GOOS}" = "windows" ]; then
|
||||||
|
zip -j "${ZIP_OUTPUT%.exe}.zip" "${OUTPUT}"
|
||||||
|
else
|
||||||
|
zip -j "${ZIP_OUTPUT}.zip" "${OUTPUT}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Created zip: ${ZIP_OUTPUT}.zip"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All builds completed successfully. Zip files are in ${ZIP_DIR}/"
|
||||||
@@ -38,7 +38,8 @@
|
|||||||
"remote": "{{.global.remote}}",
|
"remote": "{{.global.remote}}",
|
||||||
"branch": "{{.global.branch}}",
|
"branch": "{{.global.branch}}",
|
||||||
"style": "{{.global.style}}",
|
"style": "{{.global.style}}",
|
||||||
"test": "Generate test files"
|
"test": "Generate test files",
|
||||||
|
"type-group": "Generate type group files"
|
||||||
},
|
},
|
||||||
"new": {
|
"new": {
|
||||||
"short": "Fast create api service",
|
"short": "Fast create api service",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// BuildVersion is the version of goctl.
|
// BuildVersion is the version of goctl.
|
||||||
const BuildVersion = "1.8.3"
|
const BuildVersion = "1.8.4-alpha"
|
||||||
|
|
||||||
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
|
||||||
|
|
||||||
|
|||||||
@@ -1356,7 +1356,7 @@ func (p *Parser) parseKVExpression() *ast.KVExpr {
|
|||||||
expr.Colon = p.curTokenNode()
|
expr.Colon = p.curTokenNode()
|
||||||
|
|
||||||
// token STRING
|
// token STRING
|
||||||
if !p.advanceIfPeekTokenIs(token.STRING, token.RAW_STRING) {
|
if !p.advanceIfPeekTokenIs(token.STRING, token.RAW_STRING, token.IDENT) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,6 +130,8 @@ func TestParser_Parse_infoStmt(t *testing.T) {
|
|||||||
"author": `"type author here"`,
|
"author": `"type author here"`,
|
||||||
"email": `"type email here"`,
|
"email": `"type email here"`,
|
||||||
"version": `"type version here"`,
|
"version": `"type version here"`,
|
||||||
|
"enable": `true`,
|
||||||
|
"disable": `false`,
|
||||||
}
|
}
|
||||||
p := New("foo.api", infoTestAPI)
|
p := New("foo.api", infoTestAPI)
|
||||||
result := p.Parse()
|
result := p.Parse()
|
||||||
|
|||||||
@@ -4,4 +4,6 @@ info(
|
|||||||
author: "type author here"
|
author: "type author here"
|
||||||
email: "type email here"
|
email: "type email here"
|
||||||
version: "type version here"
|
version: "type version here"
|
||||||
|
enable: true
|
||||||
|
disable: false
|
||||||
)
|
)
|
||||||
@@ -10,6 +10,8 @@ info ( // info stmt
|
|||||||
author: "type author here"
|
author: "type author here"
|
||||||
email: "type email here"
|
email: "type email here"
|
||||||
version: "type version here"
|
version: "type version here"
|
||||||
|
enable: true
|
||||||
|
disable: false
|
||||||
)
|
)
|
||||||
|
|
||||||
type AliasInt int
|
type AliasInt int
|
||||||
|
|||||||
Reference in New Issue
Block a user