Compare commits
2 Commits
c2df6028b3
...
d1e77ad4e2
Author | SHA1 | Date | |
---|---|---|---|
d1e77ad4e2 | |||
cad40565b0 |
12
api/api.go
12
api/api.go
@@ -2,13 +2,17 @@ package api
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
"github.com/go-chi/docgen"
|
||||
"github.com/go-chi/render"
|
||||
)
|
||||
|
||||
var routes = flag.Bool("routes", false, "Generate API route documentation")
|
||||
|
||||
func Start() {
|
||||
flag.Parse()
|
||||
|
||||
@@ -40,5 +44,13 @@ func Start() {
|
||||
})
|
||||
})
|
||||
|
||||
if *routes {
|
||||
fmt.Println(docgen.MarkdownRoutesDoc(r, docgen.MarkdownOpts{
|
||||
ProjectPath: "git.dubyatp.xyz/chat-api-server",
|
||||
Intro: "Welcome to the chat API server. This is a simple API for sending and receiving messages.",
|
||||
}))
|
||||
return
|
||||
}
|
||||
|
||||
http.ListenAndServe(":3000", r)
|
||||
}
|
||||
|
62
api/db.go
62
api/db.go
@@ -1,21 +1,71 @@
|
||||
package api
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"git.dubyatp.xyz/chat-api-server/db"
|
||||
)
|
||||
|
||||
func dbGetUser(id int64) (*User, error) {
|
||||
data := db.ExecDB("users")
|
||||
if data == nil {
|
||||
return nil, errors.New("failed to load users database")
|
||||
}
|
||||
|
||||
users := data["users"].([]interface{})
|
||||
for _, u := range users {
|
||||
if u.ID == id {
|
||||
return u, nil
|
||||
user := u.(map[string]interface{})
|
||||
if int64(user["ID"].(float64)) == id {
|
||||
return &User{
|
||||
ID: int64(user["ID"].(float64)),
|
||||
Name: user["Name"].(string),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("User not found")
|
||||
}
|
||||
|
||||
func dbGetMessage(id string) (*Message, error) {
|
||||
for _, a := range messages {
|
||||
if a.ID == id {
|
||||
return a, nil
|
||||
data := db.ExecDB("messages")
|
||||
if data == nil {
|
||||
return nil, errors.New("failed to load messages database")
|
||||
}
|
||||
|
||||
messages := data["messages"].([]interface{})
|
||||
for _, m := range messages {
|
||||
message := m.(map[string]interface{})
|
||||
if message["ID"].(string) == id {
|
||||
return &Message{
|
||||
ID: message["ID"].(string),
|
||||
UserID: int64(message["UserID"].(float64)),
|
||||
Body: message["Body"].(string),
|
||||
Timestamp: int64(message["Timestamp"].(float64)),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Message not found")
|
||||
}
|
||||
|
||||
func dbGetAllMessages() ([]*Message, error) {
|
||||
data := db.ExecDB("messages")
|
||||
//println(data)
|
||||
if data == nil {
|
||||
return nil, errors.New("failed to load messages database")
|
||||
}
|
||||
|
||||
messages := data["messages"].([]interface{})
|
||||
var result []*Message
|
||||
for _, m := range messages {
|
||||
message := m.(map[string]interface{})
|
||||
result = append(result, &Message{
|
||||
ID: message["ID"].(string),
|
||||
UserID: int64(message["UserID"].(float64)),
|
||||
Body: message["Body"].(string),
|
||||
Timestamp: int64(message["Timestamp"].(float64)),
|
||||
})
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil, errors.New("no messages found")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
@@ -43,7 +43,12 @@ func GetMessage(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func ListMessages(w http.ResponseWriter, r *http.Request) {
|
||||
if err := render.RenderList(w, r, NewMessageListResponse(messages)); err != nil {
|
||||
dbMessages, err := dbGetAllMessages()
|
||||
if err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
if err := render.RenderList(w, r, NewMessageListResponse(dbMessages)); err != nil {
|
||||
render.Render(w, r, ErrRender(err))
|
||||
return
|
||||
}
|
||||
|
47
db/fake_db.go
Normal file
47
db/fake_db.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func ExecDB(db_name string) map[string]interface{} {
|
||||
var result map[string]interface{}
|
||||
|
||||
if db_name == "users" {
|
||||
users_db, err := os.Open("./test_data/users.json")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
fmt.Println("Successfully opened Users DB")
|
||||
defer users_db.Close()
|
||||
|
||||
byteValue, _ := io.ReadAll(users_db)
|
||||
var users []interface{}
|
||||
json.Unmarshal(byteValue, &users)
|
||||
result = map[string]interface{}{"users": users}
|
||||
|
||||
} else if db_name == "messages" {
|
||||
messages_db, err := os.Open("./test_data/messages.json")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
fmt.Println("Successfully opened Messages DB")
|
||||
defer messages_db.Close()
|
||||
|
||||
byteValue, _ := io.ReadAll(messages_db)
|
||||
var messages []interface{}
|
||||
json.Unmarshal(byteValue, &messages)
|
||||
result = map[string]interface{}{"messages": messages}
|
||||
|
||||
} else {
|
||||
fmt.Println("Invalid DB name")
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
73
docs/routes.md
Normal file
73
docs/routes.md
Normal file
@@ -0,0 +1,73 @@
|
||||
## Routes
|
||||
|
||||
<details>
|
||||
<summary>`/`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [git.dubyatp.xyz/chat-api-server/api.Start.SetContentType.func5]()
|
||||
- **/**
|
||||
- _GET_
|
||||
- [Start.func1]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/messages`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [git.dubyatp.xyz/chat-api-server/api.Start.SetContentType.func5]()
|
||||
- **/messages**
|
||||
- **/**
|
||||
- _GET_
|
||||
- [ListMessages]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/messages/{messageID}`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [git.dubyatp.xyz/chat-api-server/api.Start.SetContentType.func5]()
|
||||
- **/messages**
|
||||
- **/{messageID}**
|
||||
- [MessageCtx]()
|
||||
- **/**
|
||||
- _GET_
|
||||
- [GetMessage]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/panic`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [git.dubyatp.xyz/chat-api-server/api.Start.SetContentType.func5]()
|
||||
- **/panic**
|
||||
- _GET_
|
||||
- [Start.func3]()
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>`/ping`</summary>
|
||||
|
||||
- [RequestID]()
|
||||
- [Logger]()
|
||||
- [Recoverer]()
|
||||
- [URLFormat]()
|
||||
- [git.dubyatp.xyz/chat-api-server/api.Start.SetContentType.func5]()
|
||||
- **/ping**
|
||||
- _GET_
|
||||
- [Start.func2]()
|
||||
|
||||
</details>
|
||||
|
||||
Total # of routes: 5
|
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.23
|
||||
require (
|
||||
github.com/go-chi/chi/v5 v5.2.0
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-chi/docgen v1.3.0
|
||||
)
|
||||
|
||||
require github.com/ajg/form v1.5.1 // indirect
|
||||
|
6
go.sum
6
go.sum
@@ -1,6 +1,12 @@
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0=
|
||||
github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/docgen v1.3.0 h1:dmDJ2I+EJfCTrxfgxQDwfR/OpZLTRFKe7EKB8v7yuxI=
|
||||
github.com/go-chi/docgen v1.3.0/go.mod h1:G9W0G551cs2BFMSn/cnGwX+JBHEloAgo17MBhyrnhPI=
|
||||
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
26
test_data/messages.json
Normal file
26
test_data/messages.json
Normal file
@@ -0,0 +1,26 @@
|
||||
[
|
||||
{
|
||||
"ID": "1",
|
||||
"UserID": 1,
|
||||
"Body": "hello",
|
||||
"Timestamp": 1234567890
|
||||
},
|
||||
{
|
||||
"ID": "2",
|
||||
"UserID": 2,
|
||||
"Body": "world",
|
||||
"Timestamp": 1234567890
|
||||
},
|
||||
{
|
||||
"ID": "3",
|
||||
"UserID": 1,
|
||||
"Body": "abababa",
|
||||
"Timestamp": 1234567890
|
||||
},
|
||||
{
|
||||
"ID": "4",
|
||||
"UserID": 2,
|
||||
"Body": "bitch",
|
||||
"Timestamp": 1234567890
|
||||
}
|
||||
]
|
10
test_data/users.json
Normal file
10
test_data/users.json
Normal file
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"ID": 1,
|
||||
"Name": "duby"
|
||||
},
|
||||
{
|
||||
"ID": 2,
|
||||
"Name": "astolfo"
|
||||
}
|
||||
]
|
20
vendor/github.com/go-chi/docgen/LICENSE
generated
vendored
Normal file
20
vendor/github.com/go-chi/docgen/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2016-Present https://github.com/go-chi authors
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
126
vendor/github.com/go-chi/docgen/builder.go
generated
vendored
Normal file
126
vendor/github.com/go-chi/docgen/builder.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
package docgen
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func BuildDoc(r chi.Routes) (Doc, error) {
|
||||
d := Doc{}
|
||||
|
||||
goPath := getGoPath()
|
||||
if goPath == "" {
|
||||
return d, errors.New("docgen: unable to determine your $GOPATH")
|
||||
}
|
||||
|
||||
// Walk and generate the router docs
|
||||
d.Router = buildDocRouter(r)
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func buildDocRouter(r chi.Routes) DocRouter {
|
||||
rts := r
|
||||
dr := DocRouter{Middlewares: []DocMiddleware{}}
|
||||
drts := DocRoutes{}
|
||||
dr.Routes = drts
|
||||
|
||||
for _, mw := range rts.Middlewares() {
|
||||
dmw := DocMiddleware{
|
||||
FuncInfo: buildFuncInfo(mw),
|
||||
}
|
||||
dr.Middlewares = append(dr.Middlewares, dmw)
|
||||
}
|
||||
|
||||
for _, rt := range rts.Routes() {
|
||||
drt := DocRoute{Pattern: rt.Pattern, Handlers: DocHandlers{}}
|
||||
|
||||
if rt.SubRoutes != nil {
|
||||
subRoutes := rt.SubRoutes
|
||||
subDrts := buildDocRouter(subRoutes)
|
||||
drt.Router = &subDrts
|
||||
|
||||
} else {
|
||||
hall := rt.Handlers["*"]
|
||||
for method, h := range rt.Handlers {
|
||||
if method != "*" && hall != nil && fmt.Sprintf("%v", hall) == fmt.Sprintf("%v", h) {
|
||||
continue
|
||||
}
|
||||
|
||||
dh := DocHandler{Method: method, Middlewares: []DocMiddleware{}}
|
||||
|
||||
var endpoint http.Handler
|
||||
chain, _ := h.(*chi.ChainHandler)
|
||||
|
||||
if chain != nil {
|
||||
for _, mw := range chain.Middlewares {
|
||||
dh.Middlewares = append(dh.Middlewares, DocMiddleware{
|
||||
FuncInfo: buildFuncInfo(mw),
|
||||
})
|
||||
}
|
||||
endpoint = chain.Endpoint
|
||||
} else {
|
||||
endpoint = h
|
||||
}
|
||||
|
||||
dh.FuncInfo = buildFuncInfo(endpoint)
|
||||
|
||||
drt.Handlers[method] = dh
|
||||
}
|
||||
}
|
||||
|
||||
drts[rt.Pattern] = drt
|
||||
}
|
||||
|
||||
return dr
|
||||
}
|
||||
|
||||
func buildFuncInfo(i interface{}) FuncInfo {
|
||||
fi := FuncInfo{}
|
||||
frame := getCallerFrame(i)
|
||||
goPathSrc := filepath.Join(getGoPath(), "src")
|
||||
|
||||
if frame == nil {
|
||||
fi.Unresolvable = true
|
||||
return fi
|
||||
}
|
||||
|
||||
pkgName := getPkgName(frame.File)
|
||||
if pkgName == "chi" {
|
||||
fi.Unresolvable = true
|
||||
}
|
||||
funcPath := frame.Func.Name()
|
||||
|
||||
idx := strings.Index(funcPath, "/"+pkgName)
|
||||
if idx > 0 {
|
||||
fi.Pkg = funcPath[:idx+1+len(pkgName)]
|
||||
fi.Func = funcPath[idx+2+len(pkgName):]
|
||||
} else {
|
||||
fi.Func = funcPath
|
||||
}
|
||||
|
||||
if strings.Index(fi.Func, ".func") > 0 {
|
||||
fi.Anonymous = true
|
||||
}
|
||||
|
||||
fi.File = frame.File
|
||||
fi.Line = frame.Line
|
||||
if filepath.HasPrefix(fi.File, goPathSrc) {
|
||||
fi.File = fi.File[len(goPathSrc)+1:]
|
||||
}
|
||||
|
||||
// Check if file info is unresolvable
|
||||
if !strings.Contains(funcPath, pkgName) {
|
||||
fi.Unresolvable = true
|
||||
}
|
||||
|
||||
if !fi.Unresolvable {
|
||||
fi.Comment = getFuncComment(frame.File, frame.Line)
|
||||
}
|
||||
|
||||
return fi
|
||||
}
|
64
vendor/github.com/go-chi/docgen/docgen.go
generated
vendored
Normal file
64
vendor/github.com/go-chi/docgen/docgen.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package docgen
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type Doc struct {
|
||||
Router DocRouter `json:"router"`
|
||||
}
|
||||
|
||||
type DocRouter struct {
|
||||
Middlewares []DocMiddleware `json:"middlewares"`
|
||||
Routes DocRoutes `json:"routes"`
|
||||
}
|
||||
|
||||
type DocMiddleware struct {
|
||||
FuncInfo
|
||||
}
|
||||
|
||||
type DocRoute struct {
|
||||
Pattern string `json:"-"`
|
||||
Handlers DocHandlers `json:"handlers,omitempty"`
|
||||
Router *DocRouter `json:"router,omitempty"`
|
||||
}
|
||||
|
||||
type DocRoutes map[string]DocRoute // Pattern : DocRoute
|
||||
|
||||
type DocHandler struct {
|
||||
Middlewares []DocMiddleware `json:"middlewares"`
|
||||
Method string `json:"method"`
|
||||
FuncInfo
|
||||
}
|
||||
|
||||
type DocHandlers map[string]DocHandler // Method : DocHandler
|
||||
|
||||
func PrintRoutes(r chi.Routes) {
|
||||
var printRoutes func(parentPattern string, r chi.Routes)
|
||||
printRoutes = func(parentPattern string, r chi.Routes) {
|
||||
rts := r.Routes()
|
||||
for _, rt := range rts {
|
||||
if rt.SubRoutes == nil {
|
||||
fmt.Println(parentPattern + rt.Pattern)
|
||||
} else {
|
||||
pat := rt.Pattern
|
||||
|
||||
subRoutes := rt.SubRoutes
|
||||
printRoutes(parentPattern+pat, subRoutes)
|
||||
}
|
||||
}
|
||||
}
|
||||
printRoutes("", r)
|
||||
}
|
||||
|
||||
func JSONRoutesDoc(r chi.Routes) string {
|
||||
doc, _ := BuildDoc(r)
|
||||
v, err := json.MarshalIndent(doc, "", " ")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return string(v)
|
||||
}
|
116
vendor/github.com/go-chi/docgen/funcinfo.go
generated
vendored
Normal file
116
vendor/github.com/go-chi/docgen/funcinfo.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package docgen
|
||||
|
||||
import (
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type FuncInfo struct {
|
||||
Pkg string `json:"pkg"`
|
||||
Func string `json:"func"`
|
||||
Comment string `json:"comment"`
|
||||
File string `json:"file,omitempty"`
|
||||
Line int `json:"line,omitempty"`
|
||||
Anonymous bool `json:"anonymous,omitempty"`
|
||||
Unresolvable bool `json:"unresolvable,omitempty"`
|
||||
}
|
||||
|
||||
func GetFuncInfo(i interface{}) FuncInfo {
|
||||
fi := FuncInfo{}
|
||||
frame := getCallerFrame(i)
|
||||
goPathSrc := filepath.Join(getGoPath(), "src")
|
||||
|
||||
if frame == nil {
|
||||
fi.Unresolvable = true
|
||||
return fi
|
||||
}
|
||||
|
||||
pkgName := getPkgName(frame.File)
|
||||
if pkgName == "chi" {
|
||||
fi.Unresolvable = true
|
||||
}
|
||||
funcPath := frame.Func.Name()
|
||||
|
||||
idx := strings.Index(funcPath, "/"+pkgName)
|
||||
if idx > 0 {
|
||||
fi.Pkg = funcPath[:idx+1+len(pkgName)]
|
||||
fi.Func = funcPath[idx+2+len(pkgName):]
|
||||
} else {
|
||||
fi.Func = funcPath
|
||||
}
|
||||
|
||||
if strings.Index(fi.Func, ".func") > 0 {
|
||||
fi.Anonymous = true
|
||||
}
|
||||
|
||||
fi.File = frame.File
|
||||
fi.Line = frame.Line
|
||||
if filepath.HasPrefix(fi.File, goPathSrc) {
|
||||
fi.File = fi.File[len(goPathSrc)+1:]
|
||||
}
|
||||
|
||||
// Check if file info is unresolvable
|
||||
if strings.Index(funcPath, pkgName) < 0 {
|
||||
fi.Unresolvable = true
|
||||
}
|
||||
|
||||
if !fi.Unresolvable {
|
||||
fi.Comment = getFuncComment(frame.File, frame.Line)
|
||||
}
|
||||
|
||||
return fi
|
||||
}
|
||||
|
||||
func getCallerFrame(i interface{}) *runtime.Frame {
|
||||
value := reflect.ValueOf(i)
|
||||
if value.Kind() != reflect.Func {
|
||||
return nil
|
||||
}
|
||||
pc := value.Pointer()
|
||||
frames := runtime.CallersFrames([]uintptr{pc})
|
||||
if frames == nil {
|
||||
return nil
|
||||
}
|
||||
frame, _ := frames.Next()
|
||||
if frame.Entry == 0 {
|
||||
return nil
|
||||
}
|
||||
return &frame
|
||||
}
|
||||
|
||||
func getPkgName(file string) string {
|
||||
fset := token.NewFileSet()
|
||||
astFile, err := parser.ParseFile(fset, file, nil, parser.PackageClauseOnly)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if astFile.Name == nil {
|
||||
return ""
|
||||
}
|
||||
return astFile.Name.Name
|
||||
}
|
||||
|
||||
func getFuncComment(file string, line int) string {
|
||||
fset := token.NewFileSet()
|
||||
|
||||
astFile, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if len(astFile.Comments) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, cmt := range astFile.Comments {
|
||||
if fset.Position(cmt.End()).Line+1 == line {
|
||||
return cmt.Text()
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
218
vendor/github.com/go-chi/docgen/markdown.go
generated
vendored
Normal file
218
vendor/github.com/go-chi/docgen/markdown.go
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
package docgen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type MarkdownDoc struct {
|
||||
Opts MarkdownOpts
|
||||
Router chi.Router
|
||||
Doc Doc
|
||||
Routes map[string]DocRouter // Pattern : DocRouter
|
||||
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
type MarkdownOpts struct {
|
||||
// ProjectPath is the base Go import path of the project
|
||||
ProjectPath string
|
||||
|
||||
// Intro text included at the top of the generated markdown file.
|
||||
Intro string
|
||||
|
||||
// ForceRelativeLinks to be relative even if they're not on github
|
||||
ForceRelativeLinks bool
|
||||
|
||||
// URLMap allows specifying a map of package import paths to their link sources
|
||||
// Used for mapping vendored dependencies to their upstream sources
|
||||
// For example:
|
||||
// map[string]string{"github.com/my/package/vendor/go-chi/chi/": "https://github.com/go-chi/chi/blob/master/"}
|
||||
URLMap map[string]string
|
||||
}
|
||||
|
||||
func MarkdownRoutesDoc(r chi.Router, opts MarkdownOpts) string {
|
||||
md := &MarkdownDoc{Router: r, Opts: opts}
|
||||
if err := md.Generate(); err != nil {
|
||||
return fmt.Sprintf("ERROR: %s\n", err.Error())
|
||||
}
|
||||
return md.String()
|
||||
}
|
||||
|
||||
func (md *MarkdownDoc) String() string {
|
||||
return md.buf.String()
|
||||
}
|
||||
|
||||
func (md *MarkdownDoc) Generate() error {
|
||||
if md.Router == nil {
|
||||
return errors.New("docgen: router is nil")
|
||||
}
|
||||
|
||||
doc, err := BuildDoc(md.Router)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
md.Doc = doc
|
||||
md.buf = &bytes.Buffer{}
|
||||
md.Routes = make(map[string]DocRouter)
|
||||
|
||||
md.WriteIntro()
|
||||
md.WriteRoutes()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (md *MarkdownDoc) WriteIntro() {
|
||||
pkgName := md.Opts.ProjectPath
|
||||
md.buf.WriteString(fmt.Sprintf("# %s\n\n", pkgName))
|
||||
|
||||
intro := md.Opts.Intro
|
||||
md.buf.WriteString(fmt.Sprintf("%s\n\n", intro))
|
||||
}
|
||||
|
||||
func (md *MarkdownDoc) WriteRoutes() {
|
||||
md.buf.WriteString(fmt.Sprintf("## Routes\n\n"))
|
||||
|
||||
var buildRoutesMap func(parentPattern string, ar, nr, dr *DocRouter)
|
||||
buildRoutesMap = func(parentPattern string, ar, nr, dr *DocRouter) {
|
||||
|
||||
nr.Middlewares = append(nr.Middlewares, dr.Middlewares...)
|
||||
|
||||
for pat, rt := range dr.Routes {
|
||||
pattern := parentPattern + pat
|
||||
|
||||
nr.Routes = DocRoutes{}
|
||||
|
||||
if rt.Router != nil {
|
||||
nnr := &DocRouter{}
|
||||
nr.Routes[pat] = DocRoute{
|
||||
Pattern: pat,
|
||||
Handlers: rt.Handlers,
|
||||
Router: nnr,
|
||||
}
|
||||
buildRoutesMap(pattern, ar, nnr, rt.Router)
|
||||
|
||||
} else if len(rt.Handlers) > 0 {
|
||||
nr.Routes[pat] = DocRoute{
|
||||
Pattern: pat,
|
||||
Handlers: rt.Handlers,
|
||||
Router: nil,
|
||||
}
|
||||
|
||||
// Remove the trailing slash if the handler is a subroute for "/"
|
||||
routeKey := pattern
|
||||
if pat == "/" && len(routeKey) > 1 {
|
||||
routeKey = routeKey[:len(routeKey)-1]
|
||||
}
|
||||
md.Routes[routeKey] = copyDocRouter(*ar)
|
||||
|
||||
} else {
|
||||
panic("not possible")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Build a route tree that consists of the full route pattern
|
||||
// and the part of the tree for just that specific route, stored
|
||||
// in routes map on the markdown struct. This is the structure we
|
||||
// are going to render to markdown.
|
||||
dr := md.Doc.Router
|
||||
ar := DocRouter{}
|
||||
buildRoutesMap("", &ar, &ar, &dr)
|
||||
|
||||
// Generate the markdown to render the above structure
|
||||
var printRouter func(depth int, dr DocRouter)
|
||||
printRouter = func(depth int, dr DocRouter) {
|
||||
|
||||
tabs := ""
|
||||
for i := 0; i < depth; i++ {
|
||||
tabs += "\t"
|
||||
}
|
||||
|
||||
// Middlewares
|
||||
for _, mw := range dr.Middlewares {
|
||||
md.buf.WriteString(fmt.Sprintf("%s- [%s](%s)\n", tabs, mw.Func, md.githubSourceURL(mw.File, mw.Line)))
|
||||
}
|
||||
|
||||
// Routes
|
||||
for _, rt := range dr.Routes {
|
||||
md.buf.WriteString(fmt.Sprintf("%s- **%s**\n", tabs, normalizer(rt.Pattern)))
|
||||
|
||||
if rt.Router != nil {
|
||||
printRouter(depth+1, *rt.Router)
|
||||
} else {
|
||||
for meth, dh := range rt.Handlers {
|
||||
md.buf.WriteString(fmt.Sprintf("%s\t- _%s_\n", tabs, meth))
|
||||
|
||||
// Handler middlewares
|
||||
for _, mw := range dh.Middlewares {
|
||||
md.buf.WriteString(fmt.Sprintf("%s\t\t- [%s](%s)\n", tabs, mw.Func, md.githubSourceURL(mw.File, mw.Line)))
|
||||
}
|
||||
|
||||
// Handler endpoint
|
||||
md.buf.WriteString(fmt.Sprintf("%s\t\t- [%s](%s)\n", tabs, dh.Func, md.githubSourceURL(dh.File, dh.Line)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
routePaths := []string{}
|
||||
for pat := range md.Routes {
|
||||
routePaths = append(routePaths, pat)
|
||||
}
|
||||
sort.Strings(routePaths)
|
||||
|
||||
for _, pat := range routePaths {
|
||||
dr := md.Routes[pat]
|
||||
md.buf.WriteString(fmt.Sprintf("<details>\n"))
|
||||
md.buf.WriteString(fmt.Sprintf("<summary>`%s`</summary>\n", normalizer(pat)))
|
||||
md.buf.WriteString(fmt.Sprintf("\n"))
|
||||
printRouter(0, dr)
|
||||
md.buf.WriteString(fmt.Sprintf("\n"))
|
||||
md.buf.WriteString(fmt.Sprintf("</details>\n"))
|
||||
}
|
||||
|
||||
md.buf.WriteString(fmt.Sprintf("\n"))
|
||||
md.buf.WriteString(fmt.Sprintf("Total # of routes: %d\n", len(md.Routes)))
|
||||
|
||||
// TODO: total number of handlers..
|
||||
}
|
||||
|
||||
func (md *MarkdownDoc) githubSourceURL(file string, line int) string {
|
||||
// Currently, we only automatically link to source for github projects
|
||||
if strings.Index(file, "github.com/") != 0 && !md.Opts.ForceRelativeLinks {
|
||||
return ""
|
||||
}
|
||||
if md.Opts.ProjectPath == "" {
|
||||
return ""
|
||||
}
|
||||
for pkg, url := range md.Opts.URLMap {
|
||||
if idx := strings.Index(file, pkg); idx >= 0 {
|
||||
pos := idx + len(pkg)
|
||||
url = strings.TrimRight(url, "/")
|
||||
filepath := strings.TrimLeft(file[pos:], "/")
|
||||
return fmt.Sprintf("%s/%s#L%d", url, filepath, line)
|
||||
}
|
||||
}
|
||||
if idx := strings.Index(file, md.Opts.ProjectPath); idx >= 0 {
|
||||
// relative
|
||||
pos := idx + len(md.Opts.ProjectPath)
|
||||
return fmt.Sprintf("%s#L%d", file[pos:], line)
|
||||
}
|
||||
// absolute
|
||||
return fmt.Sprintf("https://%s#L%d", file, line)
|
||||
}
|
||||
|
||||
func normalizer(s string) string {
|
||||
if strings.Contains(s, "/*") {
|
||||
return strings.Replace(s, "/*", "", -1)
|
||||
}
|
||||
return s
|
||||
}
|
50
vendor/github.com/go-chi/docgen/util.go
generated
vendored
Normal file
50
vendor/github.com/go-chi/docgen/util.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package docgen
|
||||
|
||||
import (
|
||||
"go/build"
|
||||
"os"
|
||||
)
|
||||
|
||||
func copyDocRouter(dr DocRouter) DocRouter {
|
||||
var cloneRouter func(dr DocRouter) DocRouter
|
||||
var cloneRoutes func(drt DocRoutes) DocRoutes
|
||||
|
||||
cloneRoutes = func(drts DocRoutes) DocRoutes {
|
||||
rts := DocRoutes{}
|
||||
|
||||
for pat, drt := range drts {
|
||||
rt := DocRoute{Pattern: drt.Pattern}
|
||||
if len(drt.Handlers) > 0 {
|
||||
rt.Handlers = DocHandlers{}
|
||||
for meth, dh := range drt.Handlers {
|
||||
rt.Handlers[meth] = dh
|
||||
}
|
||||
}
|
||||
if drt.Router != nil {
|
||||
rr := cloneRouter(*drt.Router)
|
||||
rt.Router = &rr
|
||||
}
|
||||
rts[pat] = rt
|
||||
}
|
||||
|
||||
return rts
|
||||
}
|
||||
|
||||
cloneRouter = func(dr DocRouter) DocRouter {
|
||||
cr := DocRouter{}
|
||||
cr.Middlewares = make([]DocMiddleware, len(dr.Middlewares))
|
||||
copy(cr.Middlewares, dr.Middlewares)
|
||||
cr.Routes = cloneRoutes(dr.Routes)
|
||||
return cr
|
||||
}
|
||||
|
||||
return cloneRouter(dr)
|
||||
}
|
||||
|
||||
func getGoPath() string {
|
||||
goPath := os.Getenv("GOPATH")
|
||||
if goPath == "" {
|
||||
goPath = build.Default.GOPATH
|
||||
}
|
||||
return goPath
|
||||
}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@@ -5,6 +5,9 @@ github.com/ajg/form
|
||||
## explicit; go 1.14
|
||||
github.com/go-chi/chi/v5
|
||||
github.com/go-chi/chi/v5/middleware
|
||||
# github.com/go-chi/docgen v1.3.0
|
||||
## explicit; go 1.15
|
||||
github.com/go-chi/docgen
|
||||
# github.com/go-chi/render v1.0.3
|
||||
## explicit; go 1.16
|
||||
github.com/go-chi/render
|
||||
|
Reference in New Issue
Block a user