diff --git a/doc/action.go b/doc/action.go index 87995f2..c0d3e9a 100644 --- a/doc/action.go +++ b/doc/action.go @@ -5,7 +5,7 @@ import ( "strings" "text/template" - "github.com/adams-sarah/test2doc/doc/parse" + "github.com/ivaningrooves/test2doc/doc/parse" ) var ( diff --git a/doc/doc.go b/doc/doc.go index eb3d74c..df2da9a 100644 --- a/doc/doc.go +++ b/doc/doc.go @@ -10,7 +10,7 @@ import ( "strings" "text/template" - "github.com/adams-sarah/test2doc/doc/parse" + "github.com/ivaningrooves/test2doc/doc/parse" ) var ( diff --git a/doc/parse/extract.go b/doc/parse/extract.go index 9a738a1..cfede7e 100644 --- a/doc/parse/extract.go +++ b/doc/parse/extract.go @@ -44,10 +44,36 @@ func IsFuncInPkg(longFnName string) bool { return doc != nil } -// getShortFnName returns the name of the function, given -// longFnName of the form: -// github.com/adams-sarah/test2doc/example.GetWidget +// getShortFnName returns the name of the function +// without the package name so: +// github.com/user/project/package.method +// becomes +// method +// and +// github.com/user/project/package.(*type).method +// becomes +// type.method func getShortFnName(longFnName string) string { - splitName := strings.Split(longFnName, ".") - return splitName[len(splitName)-1] + + // drop anything before the last '/' + slashed := strings.Split(longFnName, "/") + last := slashed[len(slashed)-1] + + // split the final part by period + dotted := strings.Split(last, ".") + + // drop the first part which is the package name + dotted = dotted[1:] + + // loop over and drop pointer references (*v) => v + for i, p := range dotted { + if len(p) > 3 { + if p[0:2] == "(*" && p[len(p)-1] == ')' { + p = p[2 : len(p)-1] + } + } + dotted[i] = p + } + + return strings.Join(dotted, ".") } diff --git a/doc/parse/package.go b/doc/parse/package.go index 385a11b..b9c70ed 100644 --- a/doc/parse/package.go +++ b/doc/parse/package.go @@ -1,6 +1,7 @@ package parse import ( + "fmt" "go/doc" "go/parser" "go/token" @@ -13,6 +14,7 @@ var ( // go/doc stores package 'Funcs' as a slice // - we need to look up documentation by func name + funcsMap map[string]*doc.Func ) @@ -31,10 +33,29 @@ func NewPackageDoc(dir string) (*doc.Package, error) { } func setDocFuncsMap(pkgDoc *doc.Package) { - funcsMap = make(map[string]*doc.Func, len(pkgDoc.Funcs)) + typeFuncsMap := getPkgTypesFunctions(pkgDoc) + funcsMap = make(map[string]*doc.Func, len(pkgDoc.Funcs)+len(typeFuncsMap)) + for _, fn := range pkgDoc.Funcs { funcsMap[fn.Name] = fn } + + for k, fn := range typeFuncsMap { + funcsMap[k] = fn + } +} + +func getPkgTypesFunctions(pkgDoc *doc.Package) map[string]*doc.Func { + result := make(map[string]*doc.Func) + for _, t := range pkgDoc.Types { + for _, f := range t.Methods { + if f.Doc != "" { + result[fmt.Sprintf("%s.%s", t.Name, f.Name)] = f + } + } + } + + return result } func getPackageDoc(dir string) (*doc.Package, error) { diff --git a/doc/url.go b/doc/url.go index 8ebfd9c..b1c156e 100644 --- a/doc/url.go +++ b/doc/url.go @@ -5,7 +5,7 @@ import ( "net/url" "strings" - "github.com/adams-sarah/test2doc/doc/parse" + "github.com/ivaningrooves/test2doc/doc/parse" ) type URL struct { diff --git a/example/README.md b/example/README.md deleted file mode 100644 index ccc9529..0000000 --- a/example/README.md +++ /dev/null @@ -1,28 +0,0 @@ -## Generating the example apib files - -### 1. run the tests - -```bash -cd example -go test ./... -``` - -apib files are generated like so: - -``` -. -├── foos -│ ├── ... -│ └── foos.apib -└── widgets - ├── ... - └── widgets.apib -``` - -### 2. combine the doc files - -```bash -./scripts/combine.sh -``` - -final apib doc file is generated at `example/apidoc.apib` \ No newline at end of file diff --git a/example/apiary.apib b/example/apiary.apib deleted file mode 100644 index 75a7e2a..0000000 --- a/example/apiary.apib +++ /dev/null @@ -1,182 +0,0 @@ -FORMAT: 1A -HOST: http://api.foosandwidgets.com - -# Foos and Widgets API -REST API for all things foo/widget. - - -# Group Foos - -## /foos{?n} - -+ Parameters - + n: `10` (number) - -### Get Foos [GET] -retrieves the collection of Foos - - -+ Response 200 (application/json) - - - - + Body - - { - "2": { - "B": "", - "A": "", - "R": "2" - }, - "ABeeSee": { - "B": "A", - "A": "Bee", - "R": "See" - }, - "OneTwoThree": { - "B": "One", - "A": "Two", - "R": "Three" - }, - "SomethingFunForYou": { - "B": "Something", - "A": "Fun", - "R": "ForYou" - } - } - - - -## /foos/{key} - -+ Parameters - + key: `ABeeSee` (string) - -### Get Foo [GET] -retrieves a single Foo - - -+ Response 200 (application/json) - - - - + Body - - { - "B": "A", - "A": "Bee", - "R": "See" - } - - - -# Group Widgets - -## /widgets/{id} - -+ Parameters - + id: `2` (number) - -### Get Widget [GET] -retrieves a single Widget - - -+ Response 200 (application/json) - - - - + Body - - { - "Id": 2, - "Name": "Pencil", - "Role": "Utensil" - } - - - -## /widgets - -### Get Widgets [GET] -retrieves the collection of Wisdget - - -+ Response 200 (application/json) - - - - + Body - - [ - { - "Id": 0, - "Name": "Nothing", - "Role": "N/A" - }, - { - "Id": 1, - "Name": "Jibjab", - "Role": "Instrument" - }, - { - "Id": 2, - "Name": "Pencil", - "Role": "Utensil" - }, - { - "Id": 3, - "Name": "Fork", - "Role": "Utensil" - }, - { - "Id": 4, - "Name": "Password", - "Role": "Credential" - }, - { - "Id": 5, - "Name": "SpanFrankisco", - "Role": "Home" - }, - { - "Id": 6, - "Name": "Doc", - "Role": "Villain" - }, - { - "Id": 7, - "Name": "Coff3e", - "Role": "Hack" - } - ] - - -### Post Widget [POST] -adds a Widget to the collection - - -+ Request (application/json) - - - - + Body - - { - "Id": 0, - "Name": "anotherwidget", - "Role": "controller" - } - -+ Response 201 (application/json) - - - - + Body - - { - "Id": 8, - "Name": "anotherwidget", - "Role": "controller" - } - - diff --git a/example/apib.tmpl b/example/apib.tmpl deleted file mode 100644 index d96d7c1..0000000 --- a/example/apib.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -FORMAT: 1A -HOST: http://api.foosandwidgets.com - -# Foos and Widgets API -REST API for all things foo/widget. - diff --git a/example/foos/foos.apib b/example/foos/foos.apib deleted file mode 100644 index fd528e9..0000000 --- a/example/foos/foos.apib +++ /dev/null @@ -1,65 +0,0 @@ - -# Group Foos - -## /foos/{key} - -+ Parameters - + key: `ABeeSee` (string) - -### Get Foo [GET] -retrieves a single Foo - - -+ Response 200 (application/json) - - - - + Body - - { - "B": "A", - "A": "Bee", - "R": "See" - } - - - -## /foos{?n} - -+ Parameters - + n: `10` (number) - -### Get Foos [GET] -retrieves the collection of Foos - - -+ Response 200 (application/json) - - - - + Body - - { - "2": { - "B": "", - "A": "", - "R": "2" - }, - "ABeeSee": { - "B": "A", - "A": "Bee", - "R": "See" - }, - "OneTwoThree": { - "B": "One", - "A": "Two", - "R": "Three" - }, - "SomethingFunForYou": { - "B": "Something", - "A": "Fun", - "R": "ForYou" - } - } - - diff --git a/example/foos/foos.go b/example/foos/foos.go deleted file mode 100644 index 64bebdd..0000000 --- a/example/foos/foos.go +++ /dev/null @@ -1,62 +0,0 @@ -package foos - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - - "github.com/gorilla/mux" -) - -// Foo is something cool -type Foo struct { - B string - A string - R string -} - -var AllFoos map[string]Foo - -func init() { - AllFoos = map[string]Foo{ // map[key]Foo - "ABeeSee": Foo{"A", "Bee", "See"}, - "OneTwoThree": Foo{"One", "Two", "Three"}, - "SomethingFunForYou": Foo{"Something", "Fun", "ForYou"}, - "2": Foo{"", "", "2"}, - } -} - -// GetFoos retrieves the collection of Foos -func GetFoos(w http.ResponseWriter, req *http.Request) { - foosJSON, err := json.Marshal(AllFoos) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - fmt.Fprintf(w, string(foosJSON)) -} - -// GetFoo retrieves a single Foo -func GetFoo(w http.ResponseWriter, req *http.Request) { - key := mux.Vars(req)["key"] - foo, ok := AllFoos[key] - if !ok { - err := errors.New("No Foo found.") - http.Error(w, err.Error(), http.StatusNotFound) - return - } - - fooJSON, err := json.Marshal(foo) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - fmt.Fprintf(w, string(fooJSON)) -} diff --git a/example/foos/foos_test.go b/example/foos/foos_test.go deleted file mode 100644 index f215b0f..0000000 --- a/example/foos/foos_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package foos_test - -import ( - "encoding/json" - "net/http" - - "github.com/adams-sarah/test2doc/example/foos" -) - -func (t *mainSuite) TestGetFoos() { - urlPath, err := router.Get("GetFoos").URL() - t.Must(t.Nil(err)) - - resp, err := http.Get(server.URL + urlPath.String() + "?n=10") - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusOK)) - - decoder := json.NewDecoder(resp.Body) - defer resp.Body.Close() - - fs := map[string]foos.Foo{} - err = decoder.Decode(&fs) - t.Must(t.Nil(err)) - - t.Equal(len(fs), len(foos.AllFoos)) - - for k, foo := range foos.AllFoos { - t.Equal(fs[k].B, foo.B) - t.Equal(fs[k].A, foo.A) - t.Equal(fs[k].R, foo.R) - } -} - -func (t *mainSuite) TestGetFoo() { - key := "ABeeSee" - urlPath, err := router.Get("GetFoo").URL("key", key) - t.Must(t.Nil(err)) - - resp, err := http.Get(server.URL + urlPath.String()) - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusOK)) - - decoder := json.NewDecoder(resp.Body) - defer resp.Body.Close() - - var foo foos.Foo - err = decoder.Decode(&foo) - t.Must(t.Nil(err)) - - t.Equal(foo.B, foos.AllFoos[key].B) - t.Equal(foo.A, foos.AllFoos[key].A) - t.Equal(foo.R, foos.AllFoos[key].R) -} diff --git a/example/foos/init_test.go b/example/foos/init_test.go deleted file mode 100644 index a02c8bb..0000000 --- a/example/foos/init_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package foos_test - -import ( - "testing" - - "github.com/adams-sarah/prettytest" - "github.com/adams-sarah/test2doc/example" - "github.com/adams-sarah/test2doc/test" - "github.com/gorilla/mux" -) - -var router *mux.Router -var server *test.Server - -type mainSuite struct { - prettytest.Suite -} - -func TestRunner(t *testing.T) { - var err error - - router = example.NewRouter() - router.KeepContext = true - - test.RegisterURLVarExtractor(mux.Vars) - - server, err = test.NewServer(router) - if err != nil { - panic(err.Error()) - } - defer server.Finish() - - prettytest.RunWithFormatter( - t, - new(prettytest.TDDFormatter), - new(mainSuite), - ) -} diff --git a/example/scripts/combine.sh b/example/scripts/combine.sh deleted file mode 100755 index 4ae9da1..0000000 --- a/example/scripts/combine.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -e - -# find all *.apib files (after tests have run, generated files) -files=`find ./**/ -type f -name "*.apib"` - -# copy template file to new apiary.apib file -cp apib.tmpl apiary.apib - -# copy contents of each generated apib file into apiary.apib -# and delete the apib file -for f in ${files[@]}; do - cat $f >> apiary.apib - rm $f -done \ No newline at end of file diff --git a/example/server.go b/example/server.go deleted file mode 100644 index d873e8a..0000000 --- a/example/server.go +++ /dev/null @@ -1,27 +0,0 @@ -package example - -import ( - "log" - "net/http" - - "github.com/adams-sarah/test2doc/example/foos" - "github.com/adams-sarah/test2doc/example/widgets" - "github.com/gorilla/mux" -) - -func main() { - router := NewRouter() - log.Fatal(http.ListenAndServe(":8080", router)) -} - -func NewRouter() *mux.Router { - r := mux.NewRouter() - r.HandleFunc("/foos", foos.GetFoos).Methods("GET").Name("GetFoos") - r.HandleFunc("/foos/{key}", foos.GetFoo).Methods("GET").Name("GetFoo") - - r.HandleFunc("/widgets", widgets.GetWidgets).Methods("GET").Name("GetWidgets") - r.HandleFunc("/widgets", widgets.PostWidget).Methods("POST").Name("PostWidget") - r.HandleFunc("/widgets/{id}", widgets.GetWidget).Methods("GET").Name("GetWidget") - - return r -} diff --git a/example/widgets/init_test.go b/example/widgets/init_test.go deleted file mode 100644 index 4641481..0000000 --- a/example/widgets/init_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package widgets_test - -import ( - "testing" - - "github.com/adams-sarah/prettytest" - "github.com/adams-sarah/test2doc/example" - "github.com/adams-sarah/test2doc/test" - "github.com/gorilla/mux" -) - -var router *mux.Router -var server *test.Server - -type mainSuite struct { - prettytest.Suite -} - -func TestRunner(t *testing.T) { - var err error - - router = example.NewRouter() - router.KeepContext = true - - test.RegisterURLVarExtractor(mux.Vars) - - server, err = test.NewServer(router) - if err != nil { - panic(err.Error()) - } - defer server.Finish() - - prettytest.RunWithFormatter( - t, - new(prettytest.TDDFormatter), - new(mainSuite), - ) -} diff --git a/example/widgets/widgets.apib b/example/widgets/widgets.apib deleted file mode 100644 index 26276c0..0000000 --- a/example/widgets/widgets.apib +++ /dev/null @@ -1,124 +0,0 @@ - -# Group Widgets - -## /widgets/{id} - -+ Parameters - + id: `2` (number) - -### Get Widget [GET] -retrieves a single Widget - - -+ Response 200 (application/json) - - - - + Body - - { - "Id": 2, - "Name": "Pencil", - "Role": "Utensil" - } - - - -+ Response 400 (text/plain; charset=utf-8) - - + Headers - - X-Content-Type-Options: nosniff - - + Body - - strconv.ParseInt: parsing "hello": invalid syntax - - - - -## /widgets - -### Get Widgets [GET] -retrieves the collection of Wisdget - - -+ Response 200 (application/json) - - - - + Body - - [ - { - "Id": 0, - "Name": "Nothing", - "Role": "N/A" - }, - { - "Id": 1, - "Name": "Jibjab", - "Role": "Instrument" - }, - { - "Id": 2, - "Name": "Pencil", - "Role": "Utensil" - }, - { - "Id": 3, - "Name": "Fork", - "Role": "Utensil" - }, - { - "Id": 4, - "Name": "Password", - "Role": "Credential" - }, - { - "Id": 5, - "Name": "SpanFrankisco", - "Role": "Home" - }, - { - "Id": 6, - "Name": "Doc", - "Role": "Villain" - }, - { - "Id": 7, - "Name": "Coff3e", - "Role": "Hack" - } - ] - - -### Post Widget [POST] -adds a Widget to the collection - - -+ Request (application/json) - - - - + Body - - { - "Id": 0, - "Name": "anotherwidget", - "Role": "controller" - } - -+ Response 201 (application/json) - - - - + Body - - { - "Id": 8, - "Name": "anotherwidget", - "Role": "controller" - } - - diff --git a/example/widgets/widgets.go b/example/widgets/widgets.go deleted file mode 100644 index e36e1e8..0000000 --- a/example/widgets/widgets.go +++ /dev/null @@ -1,104 +0,0 @@ -package widgets - -import ( - "encoding/json" - "errors" - "fmt" - "net/http" - "strconv" - - "github.com/gorilla/mux" -) - -// Widget is a thing -type Widget struct { - Id int64 - Name string - Role string -} - -var AllWidgets []Widget - -func init() { - AllWidgets = []Widget{ - Widget{0, "Nothing", "N/A"}, - Widget{1, "Jibjab", "Instrument"}, - Widget{2, "Pencil", "Utensil"}, - Widget{3, "Fork", "Utensil"}, - Widget{4, "Password", "Credential"}, - Widget{5, "SpanFrankisco", "Home"}, - Widget{6, "Doc", "Villain"}, - Widget{7, "Coff3e", "Hack"}, - } -} - -// GetWidgets retrieves the collection of Wisdget -func GetWidgets(w http.ResponseWriter, req *http.Request) { - widgetsJSON, err := json.Marshal(AllWidgets) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - fmt.Fprintf(w, string(widgetsJSON)) -} - -// GetWidget retrieves a single Widget -func GetWidget(w http.ResponseWriter, req *http.Request) { - id, err := strconv.ParseInt(mux.Vars(req)["id"], 10, 64) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if id >= int64(len(AllWidgets)) { - http.Error(w, err.Error(), http.StatusNotFound) - return - } - - widgetJSON, err := json.Marshal(AllWidgets[id]) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - fmt.Fprintf(w, string(widgetJSON)) -} - -// PostWidget adds a Widget to the collection -func PostWidget(w http.ResponseWriter, req *http.Request) { - var widget Widget - decoder := json.NewDecoder(req.Body) - - err := decoder.Decode(&widget) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if len(widget.Name) == 0 { - err = errors.New("Widget name can't be blank.") - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // not thread safe... - widget.Id = int64(len(AllWidgets)) - AllWidgets = append(AllWidgets, widget) - - widgetJSON, err := json.Marshal(widget) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - w.WriteHeader(http.StatusCreated) - fmt.Fprintf(w, string(widgetJSON)) - -} diff --git a/example/widgets/widgets_test.go b/example/widgets/widgets_test.go deleted file mode 100644 index 3d734ae..0000000 --- a/example/widgets/widgets_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package widgets_test - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - - "github.com/adams-sarah/test2doc/example/widgets" -) - -func (t *mainSuite) TestGetWidgets() { - urlPath, err := router.Get("GetWidgets").URL() - t.Must(t.Nil(err)) - - resp, err := http.Get(server.URL + urlPath.String()) - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusOK)) - - decoder := json.NewDecoder(resp.Body) - defer resp.Body.Close() - - var ws []widgets.Widget - err = decoder.Decode(&ws) - t.Must(t.Nil(err)) - - t.Equal(len(ws), len(widgets.AllWidgets)) - t.Must(t.True(len(ws) > 2)) - - t.Equal(ws[0].Id, widgets.AllWidgets[0].Id) - t.Equal(ws[2].Name, widgets.AllWidgets[2].Name) - t.Equal(ws[1].Role, widgets.AllWidgets[1].Role) -} - -func (t *mainSuite) TestGetWidgetBadRequest() { - idStr := "hello" - - urlPath, err := router.Get("GetWidget").URL("id", idStr) - t.Must(t.Nil(err)) - - resp, err := http.Get(server.URL + urlPath.String()) - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusBadRequest)) -} - -func (t *mainSuite) TestGetWidget() { - var id int64 = 2 - idStr := fmt.Sprintf("%d", id) - - urlPath, err := router.Get("GetWidget").URL("id", idStr) - t.Must(t.Nil(err)) - - resp, err := http.Get(server.URL + urlPath.String()) - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusOK)) - - decoder := json.NewDecoder(resp.Body) - defer resp.Body.Close() - - var widget widgets.Widget - err = decoder.Decode(&widget) - t.Must(t.Nil(err)) - - t.Equal(widget.Id, widgets.AllWidgets[2].Id) - t.Equal(widget.Name, widgets.AllWidgets[2].Name) - t.Equal(widget.Role, widgets.AllWidgets[2].Role) -} - -func (t *mainSuite) TestPostWidget() { - urlPath, err := router.Get("PostWidget").URL() - t.Must(t.Nil(err)) - - widget := widgets.Widget{ - Name: "anotherwidget", - Role: "controller", - } - - jsonb, err := json.Marshal(widget) - t.Must(t.Nil(err)) - buf := bytes.NewBuffer(jsonb) - - resp, err := http.Post(server.URL+urlPath.String(), "application/json", buf) - t.Must(t.Nil(err)) - - t.Must(t.Equal(resp.StatusCode, http.StatusCreated)) - - decoder := json.NewDecoder(resp.Body) - defer resp.Body.Close() - - var respWidget widgets.Widget - err = decoder.Decode(&respWidget) - t.Must(t.Nil(err)) - - t.True(respWidget.Id > 0) - t.Equal(respWidget.Name, widget.Name) - t.Equal(respWidget.Role, widget.Role) -} diff --git a/test/responsewriter.go b/test/responsewriter.go index 0177aa6..32b6202 100644 --- a/test/responsewriter.go +++ b/test/responsewriter.go @@ -1,12 +1,11 @@ package test import ( + "github.com/ivaningrooves/test2doc/doc/parse" "log" "net/http" "net/http/httptest" "runtime" - - "github.com/adams-sarah/test2doc/doc/parse" ) type ResponseWriter struct { diff --git a/test/server.go b/test/server.go index 1ba865f..15995a6 100644 --- a/test/server.go +++ b/test/server.go @@ -5,8 +5,8 @@ import ( "net/http" "net/http/httptest" - "github.com/adams-sarah/test2doc/doc" - "github.com/adams-sarah/test2doc/doc/parse" + "github.com/ivaningrooves/test2doc/doc" + "github.com/ivaningrooves/test2doc/doc/parse" ) // resources = map[uri]Resource diff --git a/test/urlparameters.go b/test/urlparameters.go index 85d8210..4c7eefe 100644 --- a/test/urlparameters.go +++ b/test/urlparameters.go @@ -1,6 +1,6 @@ package test -import "github.com/adams-sarah/test2doc/doc/parse" +import "github.com/ivaningrooves/test2doc/doc/parse" func RegisterURLVarExtractor(fn parse.URLVarExtractor) { parse.SetURLVarExtractor(&fn)