adds api routes to documentation
This commit is contained in:
parent
b36ccae974
commit
fb668425de
14 changed files with 1457 additions and 2 deletions
8
Gopkg.lock
generated
8
Gopkg.lock
generated
|
|
@ -43,6 +43,12 @@
|
||||||
revision = "dba6525398619dead495962a916728e7ee2ca322"
|
revision = "dba6525398619dead495962a916728e7ee2ca322"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/go-chi/docgen"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "ac43d9a63f3b58b1e82922411d2de365d896ee72"
|
||||||
|
version = "v1.0.2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/go-chi/jwtauth"
|
name = "github.com/go-chi/jwtauth"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
|
|
@ -261,6 +267,6 @@
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "7eee4771041702973c1452dd07edcb18368c463ba616225a8479676443a53faf"
|
inputs-digest = "4e5e0d4a6f658ef6e0fce069907b987ec1e2459bbf2b1cfe8118caf22888a05c"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,12 @@ Easily extendible RESTful API boilerplate aiming to follow idiomatic go and best
|
||||||
- Run all migrations found in ./database/migrate with: ```go-base migrate```
|
- Run all migrations found in ./database/migrate with: ```go-base migrate```
|
||||||
- Run the application: ```go-base serve```
|
- Run the application: ```go-base serve```
|
||||||
|
|
||||||
|
Run ```go-base``` only to see the cobra generated help message.
|
||||||
|
|
||||||
|
### API Routes
|
||||||
|
|
||||||
|
Check [routes.md](routes.md) file for an overview of the provided API routes.
|
||||||
|
|
||||||
#### Demo client application
|
#### Demo client application
|
||||||
For demonstration of the login and account management features this API also serves a Single Page Application (SPA) as a Progressive Web App (PWA) done with [Quasar Framework](http://quasar-framework.org) which itself is powered by [Vue.js](https://vuejs.org). The client's source code can be found [here](https://github.com/dhax/go-base-client).
|
For demonstration of the login and account management features this API also serves a Single Page Application (SPA) as a Progressive Web App (PWA) done with [Quasar Framework](http://quasar-framework.org) which itself is powered by [Vue.js](https://vuejs.org). The client's source code can be found [here](https://github.com/dhax/go-base-client).
|
||||||
|
|
||||||
|
|
@ -51,4 +57,3 @@ EMAIL_SMTP_USER | string || email smtp username
|
||||||
EMAIL_SMTP_PASSWORD | string || email smtp password
|
EMAIL_SMTP_PASSWORD | string || email smtp password
|
||||||
EMAIL_FROM_ADDRESS | string || from address used in sending emails
|
EMAIL_FROM_ADDRESS | string || from address used in sending emails
|
||||||
EMAIL_FROM_NAME | string || from name used in sending emails
|
EMAIL_FROM_NAME | string || from name used in sending emails
|
||||||
|
|
||||||
|
|
|
||||||
72
cmd/gendoc.go
Normal file
72
cmd/gendoc.go
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/dhax/go-base/api"
|
||||||
|
"github.com/go-chi/docgen"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
routes bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// gendocCmd represents the gendoc command
|
||||||
|
var gendocCmd = &cobra.Command{
|
||||||
|
Use: "gendoc",
|
||||||
|
Short: "Generate project documentation",
|
||||||
|
Long: `A longer description that spans multiple lines and likely contains examples
|
||||||
|
and usage of using your command. For example:
|
||||||
|
|
||||||
|
Cobra is a CLI library for Go that empowers applications.
|
||||||
|
This application is a tool to generate the needed files
|
||||||
|
to quickly create a Cobra application.`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if routes {
|
||||||
|
genRoutesDoc()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(gendocCmd)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// gendocCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// gendocCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
gendocCmd.Flags().BoolVarP(&routes, "routes", "r", false, "create api routes markdown file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func genRoutesDoc() {
|
||||||
|
fmt.Println("generating routes markdown file")
|
||||||
|
api, _ := api.NewAPI()
|
||||||
|
md := docgen.MarkdownRoutesDoc(api, docgen.MarkdownOpts{
|
||||||
|
ProjectPath: "github.com/dhax/go-base",
|
||||||
|
Intro: "GoBase REST API.",
|
||||||
|
})
|
||||||
|
if err := ioutil.WriteFile("routes.md", []byte(md), 0644); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
220
routes.md
Normal file
220
routes.md
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
# github.com/dhax/go-base
|
||||||
|
|
||||||
|
GoBase REST API.
|
||||||
|
|
||||||
|
## Routes
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>`/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/***
|
||||||
|
- _GET_
|
||||||
|
- [SPAHandler.func1](/api/api.go#L101)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/admin/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/admin/***
|
||||||
|
- [RequiresRole.func1](/auth/authorizer.go#L11)
|
||||||
|
- **/**
|
||||||
|
- _GET_
|
||||||
|
- [(*API).Router.func1](/api/admin/api.go#L42)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/admin/*/accounts/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/admin/***
|
||||||
|
- [RequiresRole.func1](/auth/authorizer.go#L11)
|
||||||
|
- **/accounts/***
|
||||||
|
- **/**
|
||||||
|
- _GET_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.list)-fm](/api/admin/accounts.go#L50)
|
||||||
|
- _POST_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.create)-fm](/api/admin/accounts.go#L51)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/admin/*/accounts/*/{accountID}/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/admin/***
|
||||||
|
- [RequiresRole.func1](/auth/authorizer.go#L11)
|
||||||
|
- **/accounts/***
|
||||||
|
- **/{accountID}/***
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.accountCtx)-fm](/api/admin/accounts.go#L53)
|
||||||
|
- **/**
|
||||||
|
- _PUT_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.update)-fm](/api/admin/accounts.go#L55)
|
||||||
|
- _DELETE_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.delete)-fm](/api/admin/accounts.go#L56)
|
||||||
|
- _GET_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/admin.get)-fm](/api/admin/accounts.go#L54)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/api/*/account/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/api/***
|
||||||
|
- **/account/***
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.accountCtx)-fm](/api/app/account.go#L48)
|
||||||
|
- **/**
|
||||||
|
- _PUT_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.update)-fm](/api/app/account.go#L50)
|
||||||
|
- _DELETE_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.delete)-fm](/api/app/account.go#L51)
|
||||||
|
- _GET_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.get)-fm](/api/app/account.go#L49)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/api/*/account/*/profile`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/api/***
|
||||||
|
- **/account/***
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.accountCtx)-fm](/api/app/account.go#L48)
|
||||||
|
- **/profile**
|
||||||
|
- _PUT_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.updateProfile)-fm](/api/app/account.go#L56)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/api/*/account/*/token/{tokenID}/*`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/api/***
|
||||||
|
- **/account/***
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.accountCtx)-fm](/api/app/account.go#L48)
|
||||||
|
- **/token/{tokenID}/***
|
||||||
|
- **/**
|
||||||
|
- _PUT_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.updateToken)-fm](/api/app/account.go#L53)
|
||||||
|
- _DELETE_
|
||||||
|
- [(*AccountResource).(github.com/dhax/go-base/api/app.deleteToken)-fm](/api/app/account.go#L54)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/auth/*/login`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/auth/***
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/login**
|
||||||
|
- _POST_
|
||||||
|
- [(*Resource).(github.com/dhax/go-base/auth.login)-fm](/auth/api.go#L67)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/auth/*/logout`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/auth/***
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/logout**
|
||||||
|
- _POST_
|
||||||
|
- [Verifier.func1](/vendor/github.com/go-chi/jwtauth/jwtauth.go#L70)
|
||||||
|
- [AuthenticateRefreshJWT](/auth/authenticator.go#L66)
|
||||||
|
- [(*Resource).(github.com/dhax/go-base/auth.logout)-fm](/auth/api.go#L73)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/auth/*/refresh`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/auth/***
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/refresh**
|
||||||
|
- _POST_
|
||||||
|
- [Verifier.func1](/vendor/github.com/go-chi/jwtauth/jwtauth.go#L70)
|
||||||
|
- [AuthenticateRefreshJWT](/auth/authenticator.go#L66)
|
||||||
|
- [(*Resource).(github.com/dhax/go-base/auth.refresh)-fm](/auth/api.go#L72)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/auth/*/token`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/auth/***
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/token**
|
||||||
|
- _POST_
|
||||||
|
- [(*Resource).(github.com/dhax/go-base/auth.token)-fm](/auth/api.go#L68)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
<details>
|
||||||
|
<summary>`/ping`</summary>
|
||||||
|
|
||||||
|
- [Recoverer](/vendor/github.com/go-chi/chi/middleware/recoverer.go#L18)
|
||||||
|
- [RequestID](/vendor/github.com/go-chi/chi/middleware/request_id.go#L63)
|
||||||
|
- [DefaultCompress](/vendor/github.com/go-chi/chi/middleware/compress.go#L38)
|
||||||
|
- [Timeout.func1](/vendor/github.com/go-chi/chi/middleware/timeout.go#L33)
|
||||||
|
- [RequestLogger.func1](/vendor/github.com/go-chi/chi/middleware/logger.go#L36)
|
||||||
|
- [SetContentType.func1](/vendor/github.com/go-chi/render/content_type.go#L49)
|
||||||
|
- **/ping**
|
||||||
|
- _GET_
|
||||||
|
- [NewAPI.func2](/api/api.go#L73)
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
Total # of routes: 12
|
||||||
15
vendor/github.com/go-chi/docgen/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/go-chi/docgen/.travis.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
language: go
|
||||||
|
|
||||||
|
go:
|
||||||
|
- 1.7.x
|
||||||
|
- 1.8.x
|
||||||
|
- tip
|
||||||
|
|
||||||
|
install:
|
||||||
|
- go get -u golang.org/x/tools/cmd/goimports
|
||||||
|
|
||||||
|
script:
|
||||||
|
- go get -d -t ./...
|
||||||
|
- go test ./...
|
||||||
|
- >
|
||||||
|
goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :
|
||||||
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.
|
||||||
127
vendor/github.com/go-chi/docgen/builder.go
generated
vendored
Normal file
127
vendor/github.com/go-chi/docgen/builder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
package docgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildDoc(r chi.Routes) (Doc, error) {
|
||||||
|
d := Doc{}
|
||||||
|
|
||||||
|
goPath := os.Getenv("GOPATH")
|
||||||
|
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(os.Getenv("GOPATH"), "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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
181
vendor/github.com/go-chi/docgen/docgen_test.go
generated
vendored
Normal file
181
vendor/github.com/go-chi/docgen/docgen_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
package docgen_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/docgen"
|
||||||
|
)
|
||||||
|
|
||||||
|
// RequestID comment goes here.
|
||||||
|
func RequestID(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), "requestID", "1")
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func hubIndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s reqid:%s session:%s",
|
||||||
|
chi.URLParam(r, "hubID"), ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate docs for the MuxBig from chi/mux_test.go
|
||||||
|
func TestMuxBig(t *testing.T) {
|
||||||
|
var r, sr1, sr2, sr3, sr4, sr5, sr6 *chi.Mux
|
||||||
|
r = chi.NewRouter()
|
||||||
|
r.Use(RequestID)
|
||||||
|
|
||||||
|
// Some inline middleware, 1
|
||||||
|
// We just love Go's ast tools
|
||||||
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), "session.user", "anonymous")
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
r.Get("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("fav"))
|
||||||
|
})
|
||||||
|
r.Get("/hubs/{hubID}/view", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/view reqid:%s session:%s", chi.URLParam(r, "hubID"),
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
r.Get("/hubs/{hubID}/view/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/view/%s reqid:%s session:%s", chi.URLParamFromCtx(ctx, "hubID"),
|
||||||
|
chi.URLParam(r, "*"), ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := context.WithValue(r.Context(), "session.user", "elvis")
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/ reqid:%s session:%s", ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
r.Get("/suggestions", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/suggestions reqid:%s session:%s", ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/woot/{wootID}/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
s := fmt.Sprintf("/woot/%s/%s", chi.URLParam(r, "wootID"), chi.URLParam(r, "*"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/hubs", func(r chi.Router) {
|
||||||
|
sr1 = r.(*chi.Mux)
|
||||||
|
r.Use(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
r.Route("/{hubID}", func(r chi.Router) {
|
||||||
|
sr2 = r.(*chi.Mux)
|
||||||
|
r.Get("/", hubIndexHandler)
|
||||||
|
r.Get("/touch", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/touch reqid:%s session:%s", chi.URLParam(r, "hubID"),
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
|
||||||
|
sr3 = chi.NewRouter()
|
||||||
|
sr3.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/webhooks reqid:%s session:%s", chi.URLParam(r, "hubID"),
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
sr3.Route("/{webhookID}", func(r chi.Router) {
|
||||||
|
sr4 = r.(*chi.Mux)
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/webhooks/%s reqid:%s session:%s", chi.URLParam(r, "hubID"),
|
||||||
|
chi.URLParam(r, "webhookID"), ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: /webooks is not coming up as a subrouter here...
|
||||||
|
// we kind of want to wrap a Router... ?
|
||||||
|
// perhaps add .Router() to the middleware inline thing..
|
||||||
|
// and use that always.. or, can detect in that method..
|
||||||
|
r.Mount("/webhooks", chi.Chain(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "hook", true)))
|
||||||
|
})
|
||||||
|
}).Handler(sr3))
|
||||||
|
|
||||||
|
// HMMMM.. only let Mount() for just a Router..?
|
||||||
|
// r.Mount("/webhooks", Use(...).Router(sr3))
|
||||||
|
// ... could this work even....?
|
||||||
|
|
||||||
|
// HMMMMMMMMMMMMMMMMMMMMMMMM...
|
||||||
|
// even if Mount() were to record all subhandlers mounted, we still couldn't get at the
|
||||||
|
// routes
|
||||||
|
|
||||||
|
r.Route("/posts", func(r chi.Router) {
|
||||||
|
sr5 = r.(*chi.Mux)
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/hubs/%s/posts reqid:%s session:%s", chi.URLParam(r, "hubID"),
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Route("/folders/", func(r chi.Router) {
|
||||||
|
sr6 = r.(*chi.Mux)
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/folders/ reqid:%s session:%s",
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
r.Get("/public", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
s := fmt.Sprintf("/folders/public reqid:%s session:%s",
|
||||||
|
ctx.Value("requestID"), ctx.Value("session.user"))
|
||||||
|
w.Write([]byte(s))
|
||||||
|
})
|
||||||
|
r.Get("/in", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}).ServeHTTP)
|
||||||
|
|
||||||
|
r.With(func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "search", true)))
|
||||||
|
})
|
||||||
|
}).Get("/search", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("searching.."))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
fmt.Println(docgen.JSONRoutesDoc(r))
|
||||||
|
|
||||||
|
// docgen.PrintRoutes(r)
|
||||||
|
|
||||||
|
}
|
||||||
113
vendor/github.com/go-chi/docgen/funcinfo.go
generated
vendored
Normal file
113
vendor/github.com/go-chi/docgen/funcinfo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
package docgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"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(os.Getenv("GOPATH"), "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 {
|
||||||
|
pc := reflect.ValueOf(i).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 ""
|
||||||
|
}
|
||||||
211
vendor/github.com/go-chi/docgen/markdown.go
generated
vendored
Normal file
211
vendor/github.com/go-chi/docgen/markdown.go
generated
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
||||||
|
package docgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
)
|
||||||
|
|
||||||
|
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, 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", 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)
|
||||||
|
}
|
||||||
159
vendor/github.com/go-chi/docgen/raml/raml.go
generated
vendored
Normal file
159
vendor/github.com/go-chi/docgen/raml/raml.go
generated
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
package raml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var header = `#%RAML 1.0
|
||||||
|
---
|
||||||
|
`
|
||||||
|
|
||||||
|
type RAML struct {
|
||||||
|
Title string `yaml:"title,omitempty"`
|
||||||
|
BaseUri string `yaml:"baseUri,omitempty"`
|
||||||
|
Protocols []string `yaml:"protocols,omitempty"`
|
||||||
|
MediaType string `yaml:"mediaType,omitempty"`
|
||||||
|
Version string `yaml:"version,omitempty"`
|
||||||
|
Documentation []Documentation `yaml:"documentation,omitempty"`
|
||||||
|
|
||||||
|
Resources `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RAML) String() string {
|
||||||
|
bytes, _ := yaml.Marshal(r)
|
||||||
|
return fmt.Sprintf("%s%s", header, bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Documentation struct {
|
||||||
|
Title string `yaml:"title"`
|
||||||
|
Content string `yaml:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Resources map[string]*Resource
|
||||||
|
|
||||||
|
type Resource struct {
|
||||||
|
DisplayName string `yaml:"displayName,omitempty"`
|
||||||
|
Description string `yaml:"description,omitempty"`
|
||||||
|
Responses Responses `yaml:"responses,omitempty"`
|
||||||
|
Body Body `yaml:"body,omitempty"`
|
||||||
|
Is []string `yaml:"is,omitempty"`
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
|
SecuredBy []string `yaml:"securedBy,omitempty"`
|
||||||
|
UriParameters []string `yaml:"uirParameters,omitempty"`
|
||||||
|
QueryParameters []string `yaml:"queryParameters,omitempty"`
|
||||||
|
|
||||||
|
Resources `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Responses map[int]Response
|
||||||
|
|
||||||
|
type Response struct {
|
||||||
|
Body `yaml:"body,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Body map[string]Example // Content-Type to Example
|
||||||
|
|
||||||
|
type Example struct {
|
||||||
|
Example string `yaml:"example,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RAML) Add(method string, route string, resource *Resource) error {
|
||||||
|
if resource == nil {
|
||||||
|
return errors.New("raml.Add(): resource can't be nil")
|
||||||
|
}
|
||||||
|
if r.Resources == nil {
|
||||||
|
r.Resources = Resources{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Resources.upsert(method, route, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RAML) AddUnder(parentRoute string, method string, route string, resource *Resource) error {
|
||||||
|
if resource == nil {
|
||||||
|
return errors.New("raml.Add(): resource can't be nil")
|
||||||
|
}
|
||||||
|
if r.Resources == nil {
|
||||||
|
r.Resources = Resources{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if parentRoute == "" || parentRoute == "/" {
|
||||||
|
return errors.New("raml.AddUnderParent(): parentRoute can't be empty or '/'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(route, parentRoute) {
|
||||||
|
return errors.New("raml.AddUnderParent(): parentRoute must be present in the route string")
|
||||||
|
}
|
||||||
|
|
||||||
|
route = strings.TrimPrefix(route, parentRoute)
|
||||||
|
if route == "" {
|
||||||
|
route = "/"
|
||||||
|
}
|
||||||
|
|
||||||
|
parentNode, found := r.Resources[parentRoute]
|
||||||
|
if !found {
|
||||||
|
parentNode = &Resource{
|
||||||
|
Resources: Resources{},
|
||||||
|
Responses: Responses{},
|
||||||
|
}
|
||||||
|
r.Resources[parentRoute] = parentNode
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentNode.Resources.upsert(method, route, resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find or create node tree from a given route and inject the resource.
|
||||||
|
func (r Resources) upsert(method string, route string, resource *Resource) error {
|
||||||
|
currentNode := r
|
||||||
|
|
||||||
|
parts := strings.Split(route, "/")
|
||||||
|
if len(parts) > 0 {
|
||||||
|
last := len(parts) - 1
|
||||||
|
|
||||||
|
// Upsert route of the resource.
|
||||||
|
for _, part := range parts[:last] {
|
||||||
|
if part == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
part = "/" + part
|
||||||
|
|
||||||
|
node, found := currentNode[part]
|
||||||
|
if !found {
|
||||||
|
node = &Resource{
|
||||||
|
Resources: Resources{},
|
||||||
|
Responses: Responses{},
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode[part] = node
|
||||||
|
}
|
||||||
|
currentNode = node.Resources
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[last] != "" {
|
||||||
|
// Upsert resource into the very bottom of the node tree.
|
||||||
|
part := "/" + parts[last]
|
||||||
|
node, found := currentNode[part]
|
||||||
|
if !found {
|
||||||
|
node = &Resource{
|
||||||
|
Resources: Resources{},
|
||||||
|
Responses: Responses{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentNode[part] = node
|
||||||
|
currentNode = node.Resources
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
method = strings.ToLower(method)
|
||||||
|
if _, found := currentNode[method]; found {
|
||||||
|
return nil
|
||||||
|
// return fmt.Errorf("duplicated method route: %v %v", method, route)
|
||||||
|
}
|
||||||
|
|
||||||
|
currentNode[method] = resource
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
225
vendor/github.com/go-chi/docgen/raml/raml_test.go
generated
vendored
Normal file
225
vendor/github.com/go-chi/docgen/raml/raml_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
package raml_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/chi/middleware"
|
||||||
|
"github.com/go-chi/docgen"
|
||||||
|
"github.com/go-chi/docgen/raml"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWalkerRAML(t *testing.T) {
|
||||||
|
r := Router()
|
||||||
|
|
||||||
|
ramlDocs := &raml.RAML{
|
||||||
|
Title: "Big Mux",
|
||||||
|
BaseUri: "https://bigmux.example.com",
|
||||||
|
Version: "v1.0",
|
||||||
|
MediaType: "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := chi.Walk(r, func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error {
|
||||||
|
handlerInfo := docgen.GetFuncInfo(handler)
|
||||||
|
resource := &raml.Resource{
|
||||||
|
Description: handlerInfo.Comment,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ramlDocs.Add(method, route, resource)
|
||||||
|
}); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := yaml.Marshal(ramlDocs)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy-pasted from _examples/raml. We can't simply import it, since it's main pkg.
|
||||||
|
func Router() chi.Router {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
|
||||||
|
r.Use(middleware.RequestID)
|
||||||
|
r.Use(middleware.Logger)
|
||||||
|
r.Use(middleware.Recoverer)
|
||||||
|
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("root."))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/ping", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("pong"))
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/panic", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
panic("test")
|
||||||
|
})
|
||||||
|
|
||||||
|
// RESTy routes for "articles" resource
|
||||||
|
r.Route("/articles", func(r chi.Router) {
|
||||||
|
r.With(paginate).Get("/", ListArticles)
|
||||||
|
r.Post("/", CreateArticle) // POST /articles
|
||||||
|
r.Get("/search", SearchArticles) // GET /articles/search
|
||||||
|
|
||||||
|
r.Route("/:articleID", func(r chi.Router) {
|
||||||
|
r.Use(ArticleCtx) // Load the *Article on the request context
|
||||||
|
r.Get("/", GetArticle) // GET /articles/123
|
||||||
|
r.Put("/", UpdateArticle) // PUT /articles/123
|
||||||
|
r.Delete("/", DeleteArticle) // DELETE /articles/123
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mount the admin sub-router, the same as a call to
|
||||||
|
// Route("/admin", func(r chi.Router) { with routes here })
|
||||||
|
r.Mount("/admin", adminRouter())
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
type Article struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Article fixture data
|
||||||
|
var articles = []*Article{
|
||||||
|
{ID: "1", Title: "Hi"},
|
||||||
|
{ID: "2", Title: "sup"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ArticleCtx middleware is used to load an Article object from
|
||||||
|
// the URL parameters passed through as the request. In case
|
||||||
|
// the Article could not be found, we stop here and return a 404.
|
||||||
|
func ArticleCtx(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
articleID := chi.URLParam(r, "articleID")
|
||||||
|
article, err := dbGetArticle(articleID)
|
||||||
|
if err != nil {
|
||||||
|
render.Status(r, http.StatusNotFound)
|
||||||
|
render.JSON(w, r, http.StatusText(http.StatusNotFound))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx := context.WithValue(r.Context(), "article", article)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search Articles.
|
||||||
|
// Searches the Articles data for a matching article.
|
||||||
|
// It's just a stub, but you get the idea.
|
||||||
|
func SearchArticles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
render.JSON(w, r, articles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// List Articles.
|
||||||
|
// Returns an array of Articles.
|
||||||
|
func ListArticles(w http.ResponseWriter, r *http.Request) {
|
||||||
|
render.JSON(w, r, articles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new Article.
|
||||||
|
// Ppersists the posted Article and returns it
|
||||||
|
// back to the client as an acknowledgement.
|
||||||
|
func CreateArticle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
article := &Article{}
|
||||||
|
|
||||||
|
render.JSON(w, r, article)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a specific Article.
|
||||||
|
func GetArticle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
article := r.Context().Value("article").(*Article)
|
||||||
|
|
||||||
|
render.JSON(w, r, article)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update a specific Article.
|
||||||
|
// Updates an existing Article in our persistent store.
|
||||||
|
func UpdateArticle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
article := r.Context().Value("article").(*Article)
|
||||||
|
|
||||||
|
render.JSON(w, r, article)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a specific Article.
|
||||||
|
// Removes an existing Article from our persistent store.
|
||||||
|
func DeleteArticle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
article := r.Context().Value("article").(*Article)
|
||||||
|
|
||||||
|
render.JSON(w, r, article)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A completely separate router for administrator routes
|
||||||
|
func adminRouter() chi.Router {
|
||||||
|
r := chi.NewRouter()
|
||||||
|
r.Use(AdminOnly)
|
||||||
|
r.Get("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("admin: index"))
|
||||||
|
})
|
||||||
|
r.Get("/accounts", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte("admin: list accounts.."))
|
||||||
|
})
|
||||||
|
r.Get("/users/:userId", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Write([]byte(fmt.Sprintf("admin: view user id %v", chi.URLParam(r, "userId"))))
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// AdminOnly middleware restricts access to just administrators.
|
||||||
|
func AdminOnly(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
isAdmin, ok := r.Context().Value("acl.admin").(bool)
|
||||||
|
if !ok || !isAdmin {
|
||||||
|
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// paginate is a stub, but very possible to implement middleware logic
|
||||||
|
// to handle the request params for handling a paginated request.
|
||||||
|
func paginate(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// just a stub.. some ideas are to look at URL query params for something like
|
||||||
|
// the page number, or the limit, and send a query cursor down the chain
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
//--
|
||||||
|
|
||||||
|
// Below are a bunch of helper functions that mock some kind of storage
|
||||||
|
|
||||||
|
func dbNewArticle(article *Article) (string, error) {
|
||||||
|
article.ID = fmt.Sprintf("%d", rand.Intn(100)+10)
|
||||||
|
articles = append(articles, article)
|
||||||
|
return article.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbGetArticle(id string) (*Article, error) {
|
||||||
|
for _, a := range articles {
|
||||||
|
if a.ID == id {
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("article not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbRemoveArticle(id string) (*Article, error) {
|
||||||
|
for i, a := range articles {
|
||||||
|
if a.ID == id {
|
||||||
|
articles = append((articles)[:i], (articles)[i+1:]...)
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errors.New("article not found.")
|
||||||
|
}
|
||||||
37
vendor/github.com/go-chi/docgen/util.go
generated
vendored
Normal file
37
vendor/github.com/go-chi/docgen/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package docgen
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue