Skip to content
This repository was archived by the owner on Oct 17, 2024. It is now read-only.

Commit 5f0aeb8

Browse files
committed
WIP: parser written to read standardized sections
1 parent 10d42f5 commit 5f0aeb8

File tree

7 files changed

+317
-102
lines changed

7 files changed

+317
-102
lines changed

intermediate_api.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package main
2+
3+
import (
4+
"regexp"
5+
"bytes"
6+
"bufio"
7+
)
8+
9+
10+
type ApiIntermediate struct {
11+
ApiVersion string
12+
ApiTitle string
13+
ApiDescription string
14+
BasePath string
15+
SubApis []SubApiIntermediate
16+
}
17+
18+
19+
func intermediatateApi(commentBlocks []string) ApiIntermediate {
20+
21+
// @APIVersion 1.0.0
22+
// @APITitle REST API
23+
// @APIDescription EMS Rest API
24+
// @BasePath /api/v1
25+
// @SubApi HealthCheck [/health]
26+
27+
var (
28+
// At the time of writing, IntelliJ erroneously warns on unnecessary
29+
// escape sequences. Do not trust IntelliJ.
30+
rxApiVersion *regexp.Regexp = regexp.MustCompile(`@APIVersion\s+([\d\.]+)`)
31+
rxApiTitle *regexp.Regexp = regexp.MustCompile(`@APITitle\s+(.+)`)
32+
rxApiDescription *regexp.Regexp = regexp.MustCompile(`@APIDescription\s+(.+)`)
33+
rxBasePath *regexp.Regexp = regexp.MustCompile(`@BasePath\s+([/a-zA-Z0-9-]+)`)
34+
rxSubApi *regexp.Regexp = regexp.MustCompile(`@SubApi\s+([0-9a-zA-Z]+)\s+\[([/a-zA-Z0-9-]+)\]`)
35+
)
36+
37+
var apiIntermediate ApiIntermediate = ApiIntermediate{
38+
SubApis: make([]SubApiIntermediate, 0),
39+
}
40+
41+
for _, commentBlock := range commentBlocks {
42+
43+
b := bytes.NewBufferString(commentBlock)
44+
scanner := bufio.NewScanner(b)
45+
for scanner.Scan() {
46+
line := scanner.Text()
47+
48+
switch {
49+
50+
case rxApiDescription.MatchString(line):
51+
apiIntermediate.ApiDescription = rxApiDescription.FindStringSubmatch(line)[1]
52+
case rxApiTitle.MatchString(line):
53+
apiIntermediate.ApiTitle = rxApiTitle.FindStringSubmatch(line)[1]
54+
case rxApiVersion.MatchString(line):
55+
apiIntermediate.ApiVersion = rxApiVersion.FindStringSubmatch(line)[1]
56+
case rxBasePath.MatchString(line):
57+
apiIntermediate.BasePath = rxBasePath.FindStringSubmatch(line)[1]
58+
59+
case rxSubApi.MatchString(line):
60+
matches := rxSubApi.FindStringSubmatch(line)
61+
subApi := SubApiIntermediate{
62+
Name: matches[1],
63+
Path: matches[2],
64+
}
65+
apiIntermediate.SubApis = append(apiIntermediate.SubApis, subApi)
66+
}
67+
}
68+
}
69+
70+
return apiIntermediate
71+
}

intermediate_definition.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,14 @@ func (this *DefinitionIntermediate) DefineDefinitions() error {
125125

126126
return nil
127127
}
128+
129+
130+
131+
func mergeDefinitions(dst, src *DefinitionIntermediate) {
132+
for srcName, srcMember := range src.Members {
133+
_, exists := dst.Members[srcName]
134+
if !exists {
135+
dst.Members[srcName] = srcMember
136+
}
137+
}
138+
}
Lines changed: 43 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,6 @@ import (
99
"strings"
1010
)
1111

12-
type ApiIntermediate struct {
13-
ApiVersion string
14-
ApiTitle string
15-
ApiDescription string
16-
BasePath string
17-
SubApis []SubApiIntermediate
18-
}
19-
20-
type SubApiIntermediate struct {
21-
Name string
22-
Path string
23-
}
24-
2512
// This is an intermediate representation of a path and/or operation as parsed
2613
// in the comments. A collection of these can be combined and transformed to
2714
// create the swagger hierarchy.
@@ -63,61 +50,55 @@ func (this *ResponseIntermediate) Schema() *spec.Schema {
6350
return schema
6451
}
6552

66-
func intermediatateApi(commentBlocks []string) ApiIntermediate {
53+
// This function does not do type detection. It merely scrapes what information
54+
// there is in the comment block.
55+
func intermediatateOperation(commentBlock string) OperationIntermediate {
6756

68-
// @APIVersion 1.0.0
69-
// @APITitle REST API
70-
// @APIDescription EMS Rest API
71-
// @BasePath /api/v1
72-
// @SubApi HealthCheck [/health]
57+
/*
58+
OpenAPI Path:
59+
/api/villages
7360
74-
var (
75-
// At the time of writing, IntelliJ erroneously warns on unnecessary
76-
// escape sequences. Do not trust IntelliJ.
77-
rxApiVersion *regexp.Regexp = regexp.MustCompile(`@APIVersion\s+([\d\.]+)`)
78-
rxApiTitle *regexp.Regexp = regexp.MustCompile(`@APITitle\s+(.+)`)
79-
rxApiDescription *regexp.Regexp = regexp.MustCompile(`@APIDescription\s+(.+)`)
80-
rxBasePath *regexp.Regexp = regexp.MustCompile(`@BasePath\s+([/a-zA-Z0-9-]+)`)
81-
rxSubApi *regexp.Regexp = regexp.MustCompile(`@SubApi\s+([0-9a-zA-Z]+)\s+\[([/a-zA-Z0-9-]+)\]`)
82-
)
61+
OpenAPI Method:
62+
GET
8363
84-
var apiIntermediate ApiIntermediate = ApiIntermediate{
85-
SubApis: make([]SubApiIntermediate, 0),
86-
}
64+
OpenAPI Query String Parameters:
65+
world string required World UUID
66+
user string optional User UUID
67+
x int optional X-coordinate for blind query
68+
y int optional Y-coordinate for blind query
69+
w int optional Width of query area
70+
h int optional Height of query area
8771
88-
for _, commentBlock := range commentBlocks {
89-
90-
b := bytes.NewBufferString(commentBlock)
91-
scanner := bufio.NewScanner(b)
92-
for scanner.Scan() {
93-
line := scanner.Text()
94-
95-
switch {
96-
97-
case rxApiDescription.MatchString(line):
98-
apiIntermediate.ApiDescription = rxApiDescription.FindStringSubmatch(line)[1]
99-
case rxApiTitle.MatchString(line):
100-
apiIntermediate.ApiTitle = rxApiTitle.FindStringSubmatch(line)[1]
101-
case rxApiVersion.MatchString(line):
102-
apiIntermediate.ApiVersion = rxApiVersion.FindStringSubmatch(line)[1]
103-
case rxBasePath.MatchString(line):
104-
apiIntermediate.BasePath = rxBasePath.FindStringSubmatch(line)[1]
105-
106-
case rxSubApi.MatchString(line):
107-
matches := rxSubApi.FindStringSubmatch(line)
108-
subApi := SubApiIntermediate{
109-
Name: matches[1],
110-
Path: matches[2],
111-
}
112-
apiIntermediate.SubApis = append(apiIntermediate.SubApis, subApi)
113-
}
114-
}
115-
}
72+
OpenAPI Request Body:
73+
nil
11674
117-
return apiIntermediate
118-
}
75+
OpenAPI Response Body:
76+
[]types.Village
11977
120-
func intermediatateOperation(commentBlock string) OperationIntermediate {
78+
OpenAPI Description:
79+
This endpoint returns all of the villages that belong to the user and world
80+
specified by the query string parameter.
81+
82+
The `world` parameter is required in all uses.
83+
84+
The `user` parameter returns all villages owned by that user. If the calling
85+
player has permission to view all village information, then that information
86+
will be returned. Otherwise, only a subset of village information is
87+
returned. Use of this parameter is the recommended way to get the calling
88+
user's villages. Use of this parameter takes precedence over the use of the
89+
`x`, `y`, `w`, and `h` parameters.
90+
91+
Similar to the tiles endpoint (`/api/tiles [GET]`), the `x`, `y`, `w`, and
92+
`h` parameters control the retrieval of all the villages in a specific area
93+
of the map. Unless specified, the values for these parameters are assumed to
94+
be zero. If `w` and `h` are zero, then only one village is returned (if it
95+
exists at the coordinates provided). The maximum values accepted for `w` and
96+
`h` will be 1000, and values exceeding 1000 will be quietly accepted as
97+
1000.
98+
99+
In all circumstances, a set (array) is returned regardless of the quantity
100+
of villages returned.
101+
*/
121102

122103
// @Title Get TimeZone
123104
// @Description Return a TimeZone, given its id
@@ -253,28 +234,3 @@ func intermediatateOperation(commentBlock string) OperationIntermediate {
253234

254235
return operationIntermediate
255236
}
256-
257-
func mergeDefinitions(dst, src *DefinitionIntermediate) {
258-
for srcName, srcMember := range src.Members {
259-
_, exists := dst.Members[srcName]
260-
if !exists {
261-
dst.Members[srcName] = srcMember
262-
}
263-
}
264-
}
265-
266-
func tagOperations(apiIntermediate ApiIntermediate, operationIntermediates []OperationIntermediate) []OperationIntermediate {
267-
newOperationIntermediates := make([]OperationIntermediate, 0)
268-
269-
for _, operationIntermediate := range operationIntermediates {
270-
for _, subApi := range apiIntermediate.SubApis {
271-
if strings.HasPrefix(operationIntermediate.Path, subApi.Path) {
272-
operationIntermediate.Tag = subApi.Name
273-
break
274-
}
275-
}
276-
newOperationIntermediates = append(newOperationIntermediates, operationIntermediate)
277-
}
278-
279-
return newOperationIntermediates
280-
}

intermediate_tag.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
import "strings"
4+
5+
type TagIntermediate struct {
6+
Name string
7+
Description string
8+
}
9+
10+
func intermediatateTags(commentBlock string) []TagIntermediate {
11+
12+
tagIntermediates := make([]TagIntermediate, 0)
13+
sections := parseSections(commentBlock)
14+
15+
for _, section := range sections {
16+
17+
var tagIntermediate TagIntermediate
18+
19+
title := strings.ToLower(section.Title)
20+
if title != "openapi tag" {
21+
continue
22+
}
23+
24+
lines := strings.Split(section.Body, "\n")
25+
if len(lines) == 0 {
26+
// No body means no tag.
27+
continue
28+
}
29+
30+
// The first line of the body is the actual tag.
31+
tagIntermediate.Name = strings.TrimSpace(lines[0])
32+
33+
// The remaining lines are the tag description.
34+
if len(lines) > 1 {
35+
tagIntermediate.Description = strings.Join(lines[1:], "\n")
36+
}
37+
38+
tagIntermediates = append(tagIntermediates, tagIntermediate)
39+
}
40+
41+
return tagIntermediates
42+
43+
}

main.go

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,30 +88,53 @@ func main() {
8888
}
8989

9090
// Now, let's check all of the comment blocks we found for tags, parsing them as necessary.
91-
apiCommentBlocks := make([]string, 0)
92-
for _, commentBlocks := range packageCommentBlocks {
93-
newApiComments := detectApiCommentBlocks(commentBlocks){
94-
apiCommentBlocks = append(apiCommentBlocks, newApiComments...)
95-
}
96-
}
97-
apiIntermediate := intermediatateApi(apiCommentBlocks)
91+
var (
92+
apiCommentBlocks []string = make([]string, 0)
93+
operationCommentBlocks map[string][]string = make(map[string][]string, 0)
94+
tagCommentBlocks []string = make([]string, 0)
95+
)
9896

99-
// We need to know the package so we know where to look for the types.
100-
operationPkgComments := make(map[string][]string)
101-
for importPath, comments := range packageCommentBlocks {
102-
operationPkgComments[importPath] = hasPathComments(comments)
97+
for importPath, commentBlocks := range packageCommentBlocks {
98+
newApiCommentBlocks := detectApiCommentBlocks(commentBlocks)
99+
apiCommentBlocks = append(apiCommentBlocks, newApiCommentBlocks...)
100+
101+
newOperationCommentBlocks := detectOperationComments(commentBlocks)
102+
// We need to know the package so we know where to look for the types.
103+
operationCommentBlocks[importPath] = newOperationCommentBlocks
104+
105+
newTagCommentBlocks := detectOperationComments(commentBlocks)
106+
tagCommentBlocks = append(tagCommentBlocks, newTagCommentBlocks...)
103107
}
104108

105-
operationIntermediates := make([]OperationIntermediate, 0)
106-
for importPath, commentBlocks := range operationPkgComments {
109+
// Let's turn our detected comments into our internal, intermediate types.
110+
111+
var (
112+
apiIntermediate ApiIntermediate // There's only one.
113+
operationIntermediates []OperationIntermediate = make([]OperationIntermediate, 0)
114+
tagIntermediates []TagIntermediate = make([]TagIntermediate, 0)
115+
)
116+
117+
// This function takes all API comment blocks, as they should all condense into a single API description.
118+
apiIntermediate = intermediatateApi(apiCommentBlocks)
119+
120+
for importPath, commentBlocks := range operationCommentBlocks {
107121
for _, commentBlock := range commentBlocks {
122+
123+
// This only scrapes the information found in the comment block.
124+
// It doesn't do any further processing.
108125
operationIntermediate := intermediatateOperation(commentBlock)
126+
127+
// We need this for later.
109128
operationIntermediate.PackagePath = importPath
129+
110130
operationIntermediates = append(operationIntermediates, operationIntermediate)
111131
}
112132
}
113133

114-
operationIntermediates = tagOperations(apiIntermediate, operationIntermediates)
134+
for _, commentBlock := range tagCommentBlocks {
135+
newTagIntermediates := intermediatateTags(commentBlock)
136+
tagIntermediates = append(tagIntermediates, newTagIntermediates...)
137+
}
115138

116139
err = deriveDefinitionsFromOperations(operationIntermediates)
117140
if err != nil {

0 commit comments

Comments
 (0)