Переглянути джерело

add anonymous annotation (#134)

* rebase upstream

* rebase

* trim no need line

* trim no need line

* trim no need line

* update doc

* remove update

* remove no need

* remove no need

* goctl add jwt support

* goctl add jwt support

* goctl add jwt support

* goctl support import

* goctl support import

* support return ()

* revert

* refactor and rename folder to group

* remove no need

* add anonymous annotation

* optimized

* rename

* rename

* update test

* optimized new command

Co-authored-by: kingxt <dream4kingxt@163.com>
kingxt 4 роки тому
батько
коміт
1d9c4a4c4b

+ 1 - 1
tools/goctl/api/format/format.go

@@ -65,7 +65,7 @@ func ApiFormat(path string, printToConsole bool) error {
 		return m
 	})
 
-	apiStruct, err := parser.MatchStruct(r)
+	apiStruct, err := parser.ParseApi(r)
 	if err != nil {
 		return err
 	}

+ 1 - 3
tools/goctl/api/new/newservice.go

@@ -20,9 +20,7 @@ type Response struct {
 }
 
 service {{.name}}-api {
-  @server(
-    handler: GreetHandler
-  )
+  @handler GreetHandler
   get /greet/from/:name(Request) returns (Response);
 }
 `

+ 13 - 0
tools/goctl/api/parser/entity.go

@@ -84,6 +84,18 @@ memberLoop:
 					if builder.Len() == 0 {
 						return errors.New("invalid annotation format")
 					}
+					if len(annoName) > 0 {
+						value := builder.String()
+						if value != string(leftParenthesis) {
+							builder.Reset()
+							annos = append(annos, spec.Annotation{
+								Name:  annoName,
+								Value: value,
+							})
+							annoName = ""
+							break annotationLoop
+						}
+					}
 				case next == leftParenthesis:
 					if builder.Len() == 0 {
 						return errors.New("invalid annotation format")
@@ -101,6 +113,7 @@ memberLoop:
 						Name:       annoName,
 						Properties: attrs,
 					})
+					annoName = ""
 					break annotationLoop
 				default:
 					builder.WriteRune(next)

+ 4 - 2
tools/goctl/api/parser/parser.go

@@ -16,6 +16,7 @@ import (
 type Parser struct {
 	r       *bufio.Reader
 	typeDef string
+	api     *ApiStruct
 }
 
 func NewParser(filename string) (*Parser, error) {
@@ -29,7 +30,7 @@ func NewParser(filename string) (*Parser, error) {
 		return nil, err
 	}
 
-	apiStruct, err := MatchStruct(string(api))
+	apiStruct, err := ParseApi(string(api))
 	if err != nil {
 		return nil, err
 	}
@@ -55,6 +56,7 @@ func NewParser(filename string) (*Parser, error) {
 	return &Parser{
 		r:       bufio.NewReader(buffer),
 		typeDef: apiStruct.StructBody,
+		api:     apiStruct,
 	}, nil
 }
 
@@ -66,7 +68,7 @@ func (p *Parser) Parse() (api *spec.ApiSpec, err error) {
 		return nil, err
 	}
 	api.Types = types
-	var lineNumber = 1
+	var lineNumber = p.api.serviceBeginLine
 	st := newRootState(p.r, &lineNumber)
 	for {
 		st, err = st.process(api)

+ 35 - 1
tools/goctl/api/parser/parser_test.go

@@ -104,6 +104,21 @@ service A-api
 }
 `
 
+const anonymousAnnotation = `
+type Request struct {
+  Name string ` + "`" + `path:"name,options=you|me"` + "`" + `
+}
+
+type Response struct {
+  Message string ` + "`" + `json:"message"` + "`" + `
+}
+
+service A-api {
+  @handler GreetHandler
+  get /greet/from/:name(Request) returns (Response)
+}
+`
+
 func TestParser(t *testing.T) {
 	filename := "greet.api"
 	err := ioutil.WriteFile(filename, []byte(testApiTemplate), os.ModePerm)
@@ -161,6 +176,25 @@ func TestInvalidApiFile(t *testing.T) {
 	assert.Nil(t, err)
 	defer os.Remove(filename)
 
-	_, err = NewParser(filename)
+	parser, err := NewParser(filename)
+	assert.Nil(t, err)
+
+	_, err = parser.Parse()
 	assert.NotNil(t, err)
 }
+
+func TestAnonymousAnnotation(t *testing.T) {
+	filename := "greet.api"
+	err := ioutil.WriteFile(filename, []byte(anonymousAnnotation), os.ModePerm)
+	assert.Nil(t, err)
+	defer os.Remove(filename)
+
+	parser, err := NewParser(filename)
+	assert.Nil(t, err)
+
+	api, err := parser.Parse()
+	assert.Nil(t, err)
+
+	assert.Equal(t, len(api.Service.Routes), 1)
+	assert.Equal(t, api.Service.Routes[0].Annotations[0].Value, "GreetHandler")
+}

+ 21 - 6
tools/goctl/api/parser/util.go

@@ -11,10 +11,11 @@ import (
 var emptyType spec.Type
 
 type ApiStruct struct {
-	Info       string
-	StructBody string
-	Service    string
-	Imports    string
+	Info             string
+	StructBody       string
+	Service          string
+	Imports          string
+	serviceBeginLine int
 }
 
 func GetType(api *spec.ApiSpec, t string) spec.Type {
@@ -69,7 +70,7 @@ func unread(r *bufio.Reader) error {
 	return r.UnreadRune()
 }
 
-func MatchStruct(api string) (*ApiStruct, error) {
+func ParseApi(api string) (*ApiStruct, error) {
 	var result ApiStruct
 	scanner := bufio.NewScanner(strings.NewReader(api))
 	var parseInfo = false
@@ -104,13 +105,13 @@ func MatchStruct(api string) (*ApiStruct, error) {
 			parseType = true
 		}
 		if isServiceBeginLine(line) {
+			parseService = true
 			if parseType {
 				parseType = false
 				result.StructBody = segment
 				segment = line + "\n"
 				continue
 			}
-			parseService = true
 		}
 		segment += scanner.Text() + "\n"
 	}
@@ -119,6 +120,7 @@ func MatchStruct(api string) (*ApiStruct, error) {
 		return nil, errors.New("no service defined")
 	}
 	result.Service = segment
+	result.serviceBeginLine = lineBeginOfService(api)
 	return &result, nil
 }
 
@@ -133,3 +135,16 @@ func isTypeBeginLine(line string) bool {
 func isServiceBeginLine(line string) bool {
 	return strings.HasPrefix(line, "@server(") || (strings.HasPrefix(line, "service") && strings.HasSuffix(line, "{"))
 }
+
+func lineBeginOfService(api string) int {
+	scanner := bufio.NewScanner(strings.NewReader(api))
+	var number = 0
+	for scanner.Scan() {
+		line := strings.TrimSpace(scanner.Text())
+		if isServiceBeginLine(line) {
+			break
+		}
+		number++
+	}
+	return number
+}

+ 1 - 0
tools/goctl/api/spec/spec.go

@@ -4,6 +4,7 @@ type (
 	Annotation struct {
 		Name       string
 		Properties map[string]string
+		Value      string
 	}
 
 	ApiSpec struct {

+ 3 - 0
tools/goctl/api/util/annotation.go

@@ -8,6 +8,9 @@ import (
 
 func GetAnnotationValue(annos []spec.Annotation, key, field string) (string, bool) {
 	for _, anno := range annos {
+		if anno.Name == field && len(anno.Value) > 0 {
+			return anno.Value, true
+		}
 		if anno.Name == key {
 			value, ok := anno.Properties[field]
 			return strings.TrimSpace(value), ok