vendor dependencies with dep

This commit is contained in:
dhax 2017-09-25 20:20:52 +02:00
parent 93d8310491
commit 1384296a47
2712 changed files with 965742 additions and 0 deletions

515
vendor/github.com/go-chi/chi/_examples/rest/main.go generated vendored Normal file
View file

@ -0,0 +1,515 @@
//
// REST
// ====
// This example demonstrates a HTTP REST web service with some fixture data.
// Follow along the example and patterns.
//
// Also check routes.json for the generated docs from passing the -routes flag
//
// Boot the server:
// ----------------
// $ go run main.go
//
// Client requests:
// ----------------
// $ curl http://localhost:3333/
// root.
//
// $ curl http://localhost:3333/articles
// [{"id":"1","title":"Hi"},{"id":"2","title":"sup"}]
//
// $ curl http://localhost:3333/articles/1
// {"id":"1","title":"Hi"}
//
// $ curl -X DELETE http://localhost:3333/articles/1
// {"id":"1","title":"Hi"}
//
// $ curl http://localhost:3333/articles/1
// "Not Found"
//
// $ curl -X POST -d '{"id":"will-be-omitted","title":"awesomeness"}' http://localhost:3333/articles
// {"id":"97","title":"awesomeness"}
//
// $ curl http://localhost:3333/articles/97
// {"id":"97","title":"awesomeness"}
//
// $ curl http://localhost:3333/articles
// [{"id":"2","title":"sup"},{"id":"97","title":"awesomeness"}]
//
package main
import (
"context"
"errors"
"flag"
"fmt"
"math/rand"
"net/http"
"strings"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/docgen"
"github.com/go-chi/render"
)
var routes = flag.Bool("routes", false, "Generate router documentation")
func main() {
flag.Parse()
r := chi.NewRouter()
r.Use(middleware.RequestID)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.URLFormat)
r.Use(render.SetContentType(render.ContentTypeJSON))
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
})
// GET /articles/whats-up
r.With(ArticleCtx).Get("/{articleSlug:[a-z-]+}", GetArticle)
})
// Mount the admin sub-router, which btw is the same as:
// r.Route("/admin", func(r chi.Router) { admin routes here })
r.Mount("/admin", adminRouter())
// Passing -routes to the program will generate docs for the above
// router definition. See the `routes.json` file in this folder for
// the output.
if *routes {
// fmt.Println(docgen.JSONRoutesDoc(r))
fmt.Println(docgen.MarkdownRoutesDoc(r, docgen.MarkdownOpts{
ProjectPath: "github.com/go-chi/chi",
Intro: "Welcome to the chi/_examples/rest generated docs.",
}))
return
}
http.ListenAndServe(":3333", r)
}
func ListArticles(w http.ResponseWriter, r *http.Request) {
if err := render.RenderList(w, r, NewArticleListResponse(articles)); err != nil {
render.Render(w, r, ErrRender(err))
return
}
}
// 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) {
var article *Article
var err error
if articleID := chi.URLParam(r, "articleID"); articleID != "" {
article, err = dbGetArticle(articleID)
} else if articleSlug := chi.URLParam(r, "articleSlug"); articleSlug != "" {
article, err = dbGetArticleBySlug(articleSlug)
} else {
render.Render(w, r, ErrNotFound)
return
}
if err != nil {
render.Render(w, r, ErrNotFound)
return
}
ctx := context.WithValue(r.Context(), "article", article)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// SearchArticles 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.RenderList(w, r, NewArticleListResponse(articles))
}
// CreateArticle persists the posted Article and returns it
// back to the client as an acknowledgement.
func CreateArticle(w http.ResponseWriter, r *http.Request) {
data := &ArticleRequest{}
if err := render.Bind(r, data); err != nil {
render.Render(w, r, ErrInvalidRequest(err))
return
}
article := data.Article
dbNewArticle(article)
render.Status(r, http.StatusCreated)
render.Render(w, r, NewArticleResponse(article))
}
// GetArticle returns the specific Article. You'll notice it just
// fetches the Article right off the context, as its understood that
// if we made it this far, the Article must be on the context. In case
// its not due to a bug, then it will panic, and our Recoverer will save us.
func GetArticle(w http.ResponseWriter, r *http.Request) {
// Assume if we've reach this far, we can access the article
// context because this handler is a child of the ArticleCtx
// middleware. The worst case, the recoverer middleware will save us.
article := r.Context().Value("article").(*Article)
if err := render.Render(w, r, NewArticleResponse(article)); err != nil {
render.Render(w, r, ErrRender(err))
return
}
}
// UpdateArticle updates an existing Article in our persistent store.
func UpdateArticle(w http.ResponseWriter, r *http.Request) {
article := r.Context().Value("article").(*Article)
data := &ArticleRequest{Article: article}
if err := render.Bind(r, data); err != nil {
render.Render(w, r, ErrInvalidRequest(err))
return
}
article = data.Article
dbUpdateArticle(article.ID, article)
render.Render(w, r, NewArticleResponse(article))
}
// DeleteArticle removes an existing Article from our persistent store.
func DeleteArticle(w http.ResponseWriter, r *http.Request) {
var err error
// Assume if we've reach this far, we can access the article
// context because this handler is a child of the ArticleCtx
// middleware. The worst case, the recoverer middleware will save us.
article := r.Context().Value("article").(*Article)
article, err = dbRemoveArticle(article.ID)
if err != nil {
render.Render(w, r, ErrInvalidRequest(err))
return
}
render.Render(w, r, NewArticleResponse(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)
})
}
// This is entirely optional, but I wanted to demonstrate how you could easily
// add your own logic to the render.Respond method.
func init() {
render.Respond = func(w http.ResponseWriter, r *http.Request, v interface{}) {
if err, ok := v.(error); ok {
// We set a default error status response code if one hasn't been set.
if _, ok := r.Context().Value(render.StatusCtxKey).(int); !ok {
w.WriteHeader(400)
}
// We log the error
fmt.Printf("Logging err: %s\n", err.Error())
// We change the response to not reveal the actual error message,
// instead we can transform the message something more friendly or mapped
// to some code / language, etc.
render.DefaultResponder(w, r, render.M{"status": "error"})
return
}
render.DefaultResponder(w, r, v)
}
}
//--
// Request and Response payloads for the REST api.
//
// The payloads embed the data model objects an
//
// In a real-world project, it would make sense to put these payloads
// in another file, or another sub-package.
//--
type UserPayload struct {
*User
Role string `json:"role"`
}
func NewUserPayloadResponse(user *User) *UserPayload {
return &UserPayload{User: user}
}
// Bind on UserPayload will run after the unmarshalling is complete, its
// a good time to focus some post-processing after a decoding.
func (u *UserPayload) Bind(r *http.Request) error {
return nil
}
func (u *UserPayload) Render(w http.ResponseWriter, r *http.Request) error {
u.Role = "collaborator"
return nil
}
// ArticleRequest is the request payload for Article data model.
//
// NOTE: It's good practice to have well defined request and response payloads
// so you can manage the specific inputs and outputs for clients, and also gives
// you the opportunity to transform data on input or output, for example
// on request, we'd like to protect certain fields and on output perhaps
// we'd like to include a computed field based on other values that aren't
// in the data model. Also, check out this awesome blog post on struct composition:
// http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/
type ArticleRequest struct {
*Article
User *UserPayload `json:"user,omitempty"`
ProtectedID string `json:"id"` // override 'id' json to have more control
}
func (a *ArticleRequest) Bind(r *http.Request) error {
// just a post-process after a decode..
a.ProtectedID = "" // unset the protected ID
a.Article.Title = strings.ToLower(a.Article.Title) // as an example, we down-case
return nil
}
// ArticleResponse is the response payload for the Article data model.
// See NOTE above in ArticleRequest as well.
//
// In the ArticleResponse object, first a Render() is called on itself,
// then the next field, and so on, all the way down the tree.
// Render is called in top-down order, like a http handler middleware chain.
type ArticleResponse struct {
*Article
User *UserPayload `json:"user,omitempty"`
// We add an additional field to the response here.. such as this
// elapsed computed property
Elapsed int64 `json:"elapsed"`
}
func NewArticleResponse(article *Article) *ArticleResponse {
resp := &ArticleResponse{Article: article}
if resp.User == nil {
if user, _ := dbGetUser(resp.UserID); user != nil {
resp.User = NewUserPayloadResponse(user)
}
}
return resp
}
func (rd *ArticleResponse) Render(w http.ResponseWriter, r *http.Request) error {
// Pre-processing before a response is marshalled and sent across the wire
rd.Elapsed = 10
return nil
}
type ArticleListResponse []*ArticleResponse
func NewArticleListResponse(articles []*Article) []render.Renderer {
list := []render.Renderer{}
for _, article := range articles {
list = append(list, NewArticleResponse(article))
}
return list
}
// NOTE: as a thought, the request and response payloads for an Article could be the
// same payload type, perhaps will do an example with it as well.
// type ArticlePayload struct {
// *Article
// }
//--
// Error response payloads & renderers
//--
// ErrResponse renderer type for handling all sorts of errors.
//
// In the best case scenario, the excellent github.com/pkg/errors package
// helps reveal information on the error, setting it on Err, and in the Render()
// method, using it to set the application-specific error code in AppCode.
type ErrResponse struct {
Err error `json:"-"` // low-level runtime error
HTTPStatusCode int `json:"-"` // http response status code
StatusText string `json:"status"` // user-level status message
AppCode int64 `json:"code,omitempty"` // application-specific error code
ErrorText string `json:"error,omitempty"` // application-level error message, for debugging
}
func (e *ErrResponse) Render(w http.ResponseWriter, r *http.Request) error {
render.Status(r, e.HTTPStatusCode)
return nil
}
func ErrInvalidRequest(err error) render.Renderer {
return &ErrResponse{
Err: err,
HTTPStatusCode: 400,
StatusText: "Invalid request.",
ErrorText: err.Error(),
}
}
func ErrRender(err error) render.Renderer {
return &ErrResponse{
Err: err,
HTTPStatusCode: 422,
StatusText: "Error rendering response.",
ErrorText: err.Error(),
}
}
var ErrNotFound = &ErrResponse{HTTPStatusCode: 404, StatusText: "Resource not found."}
//--
// Data model objects and persistence mocks:
//--
// User data model
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
// Article data model. I suggest looking at https://upper.io for an easy
// and powerful data persistence adapter.
type Article struct {
ID string `json:"id"`
UserID int64 `json:"user_id"` // the author
Title string `json:"title"`
Slug string `json:"slug"`
}
// Article fixture data
var articles = []*Article{
{ID: "1", UserID: 100, Title: "Hi", Slug: "hi"},
{ID: "2", UserID: 200, Title: "sup", Slug: "sup"},
{ID: "3", UserID: 300, Title: "alo", Slug: "alo"},
{ID: "4", UserID: 400, Title: "bonjour", Slug: "bonjour"},
{ID: "5", UserID: 500, Title: "whats up", Slug: "whats-up"},
}
// User fixture data
var users = []*User{
{ID: 100, Name: "Peter"},
{ID: 200, Name: "Julia"},
}
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 dbGetArticleBySlug(slug string) (*Article, error) {
for _, a := range articles {
if a.Slug == slug {
return a, nil
}
}
return nil, errors.New("article not found.")
}
func dbUpdateArticle(id string, article *Article) (*Article, error) {
for i, a := range articles {
if a.ID == id {
articles[i] = article
return article, 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.")
}
func dbGetUser(id int64) (*User, error) {
for _, u := range users {
if u.ID == id {
return u, nil
}
}
return nil, errors.New("user not found.")
}

260
vendor/github.com/go-chi/chi/_examples/rest/routes.json generated vendored Normal file
View file

@ -0,0 +1,260 @@
{
"router": {
"middlewares": [
{
"pkg": "github.com/go-chi/chi/middleware",
"func": "RequestID",
"comment": "RequestID is a middleware that injects a request ID into the context of each\nrequest. A request ID is a string of the form \"host.example.com/random-0001\",\nwhere \"random\" is a base62 random string that uniquely identifies this go\nprocess, and where the last number is an atomically incremented request\ncounter.\n",
"file": "github.com/go-chi/chi/middleware/request_id.go",
"line": 63
},
{
"pkg": "github.com/go-chi/chi/middleware",
"func": "Logger",
"comment": "Logger is a middleware that logs the start and end of each request, along\nwith some useful data about what was requested, what the response status was,\nand how long it took to return. When standard output is a TTY, Logger will\nprint in color, otherwise it will print in black and white. Logger prints a\nrequest ID if one is provided.\n\nAlternatively, look at https://github.com/pressly/lg and the `lg.RequestLogger`\nmiddleware pkg.\n",
"file": "github.com/go-chi/chi/middleware/logger.go",
"line": 26
},
{
"pkg": "github.com/go-chi/chi/middleware",
"func": "Recoverer",
"comment": "Recoverer is a middleware that recovers from panics, logs the panic (and a\nbacktrace), and returns a HTTP 500 (Internal Server Error) status if\npossible. Recoverer prints a request ID if one is provided.\n\nAlternatively, look at https://github.com/pressly/lg middleware pkgs.\n",
"file": "github.com/go-chi/chi/middleware/recoverer.go",
"line": 18
},
{
"pkg": "github.com/go-chi/chi/middleware",
"func": "URLFormat",
"comment": "URLFormat is a middleware that parses the url extension from a request path and stores it\non the context as a string under the key `middleware.URLFormatCtxKey`. The middleware will\ntrim the suffix from the routing path and continue routing.\n\nRouters should not include a url parameter for the suffix when using this middleware.\n\nSample usage.. for url paths: `/articles/1`, `/articles/1.json` and `/articles/1.xml`\n\n func routes() http.Handler {\n r := chi.NewRouter()\n r.Use(middleware.URLFormat)\n\n r.Get(\"/articles/{id}\", ListArticles)\n\n return r\n }\n\n func ListArticles(w http.ResponseWriter, r *http.Request) {\n\t urlFormat, _ := r.Context().Value(middleware.URLFormatCtxKey).(string)\n\n\t switch urlFormat {\n\t case \"json\":\n\t \trender.JSON(w, r, articles)\n\t case \"xml:\"\n\t \trender.XML(w, r, articles)\n\t default:\n\t \trender.JSON(w, r, articles)\n\t }\n}\n",
"file": "github.com/go-chi/chi/middleware/url_format.go",
"line": 45
},
{
"pkg": "github.com/go-chi/render",
"func": "SetContentType.func1",
"comment": "",
"file": "github.com/go-chi/render/content_type.go",
"line": 49,
"anonymous": true
}
],
"routes": {
"/": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.main.func1",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 69,
"anonymous": true
}
}
},
"/admin/*": {
"router": {
"middlewares": [
{
"pkg": "",
"func": "main.AdminOnly",
"comment": "AdminOnly middleware restricts access to just administrators.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 238
}
],
"routes": {
"/": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.adminRouter.func1",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 225,
"anonymous": true
}
}
},
"/accounts": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.adminRouter.func2",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 228,
"anonymous": true
}
}
},
"/users/{userId}": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.adminRouter.func3",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 231,
"anonymous": true
}
}
}
}
}
},
"/articles/*": {
"router": {
"middlewares": [],
"routes": {
"/": {
"handlers": {
"GET": {
"middlewares": [
{
"pkg": "",
"func": "main.paginate",
"comment": "paginate is a stub, but very possible to implement middleware logic\nto handle the request params for handling a paginated request.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 251
}
],
"method": "GET",
"pkg": "",
"func": "main.ListArticles",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 117
},
"POST": {
"middlewares": [],
"method": "POST",
"pkg": "",
"func": "main.CreateArticle",
"comment": "CreateArticle persists the posted Article and returns it\nback to the client as an acknowledgement.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 158
}
}
},
"/search": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.SearchArticles",
"comment": "SearchArticles searches the Articles data for a matching article.\nIt's just a stub, but you get the idea.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 152
}
}
},
"/{articleID}/*": {
"router": {
"middlewares": [
{
"pkg": "",
"func": "main.ArticleCtx",
"comment": "ArticleCtx middleware is used to load an Article object from\nthe URL parameters passed through as the request. In case\nthe Article could not be found, we stop here and return a 404.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 127
}
],
"routes": {
"/": {
"handlers": {
"DELETE": {
"middlewares": [],
"method": "DELETE",
"pkg": "",
"func": "main.DeleteArticle",
"comment": "DeleteArticle removes an existing Article from our persistent store.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 204
},
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.GetArticle",
"comment": "GetArticle returns the specific Article. You'll notice it just\nfetches the Article right off the context, as its understood that\nif we made it this far, the Article must be on the context. In case\nits not due to a bug, then it will panic, and our Recoverer will save us.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 176
},
"PUT": {
"middlewares": [],
"method": "PUT",
"pkg": "",
"func": "main.UpdateArticle",
"comment": "UpdateArticle updates an existing Article in our persistent store.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 189
}
}
}
}
}
},
"/{articleSlug:[a-z-]+}": {
"handlers": {
"GET": {
"middlewares": [
{
"pkg": "",
"func": "main.ArticleCtx",
"comment": "ArticleCtx middleware is used to load an Article object from\nthe URL parameters passed through as the request. In case\nthe Article could not be found, we stop here and return a 404.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 127
}
],
"method": "GET",
"pkg": "",
"func": "main.GetArticle",
"comment": "GetArticle returns the specific Article. You'll notice it just\nfetches the Article right off the context, as its understood that\nif we made it this far, the Article must be on the context. In case\nits not due to a bug, then it will panic, and our Recoverer will save us.\n",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 176
}
}
}
}
}
},
"/panic": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.main.func3",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 77,
"anonymous": true
}
}
},
"/ping": {
"handlers": {
"GET": {
"middlewares": [],
"method": "GET",
"pkg": "",
"func": "main.main.func2",
"comment": "",
"file": "github.com/go-chi/chi/_examples/rest/main.go",
"line": 73,
"anonymous": true
}
}
}
}
}
}

159
vendor/github.com/go-chi/chi/_examples/rest/routes.md generated vendored Normal file
View file

@ -0,0 +1,159 @@
# github.com/go-chi/chi
Welcome to the chi/_examples/rest generated docs.
## Routes
<details>
<summary>`/`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/**
- _GET_
- [main.main.func1](/_examples/rest/main.go#L69)
</details>
<details>
<summary>`/admin/*`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/admin/***
- [main.AdminOnly](/_examples/rest/main.go#L238)
- **/**
- _GET_
- [main.adminRouter.func1](/_examples/rest/main.go#L225)
</details>
<details>
<summary>`/admin/*/accounts`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/admin/***
- [main.AdminOnly](/_examples/rest/main.go#L238)
- **/accounts**
- _GET_
- [main.adminRouter.func2](/_examples/rest/main.go#L228)
</details>
<details>
<summary>`/admin/*/users/{userId}`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/admin/***
- [main.AdminOnly](/_examples/rest/main.go#L238)
- **/users/{userId}**
- _GET_
- [main.adminRouter.func3](/_examples/rest/main.go#L231)
</details>
<details>
<summary>`/articles/*`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/articles/***
- **/**
- _GET_
- [main.paginate](/_examples/rest/main.go#L251)
- [main.ListArticles](/_examples/rest/main.go#L117)
- _POST_
- [main.CreateArticle](/_examples/rest/main.go#L158)
</details>
<details>
<summary>`/articles/*/search`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/articles/***
- **/search**
- _GET_
- [main.SearchArticles](/_examples/rest/main.go#L152)
</details>
<details>
<summary>`/articles/*/{articleID}/*`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/articles/***
- **/{articleID}/***
- [main.ArticleCtx](/_examples/rest/main.go#L127)
- **/**
- _DELETE_
- [main.DeleteArticle](/_examples/rest/main.go#L204)
- _GET_
- [main.GetArticle](/_examples/rest/main.go#L176)
- _PUT_
- [main.UpdateArticle](/_examples/rest/main.go#L189)
</details>
<details>
<summary>`/articles/*/{articleSlug:[a-z-]+}`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/articles/***
- **/{articleSlug:[a-z-]+}**
- _GET_
- [main.ArticleCtx](/_examples/rest/main.go#L127)
- [main.GetArticle](/_examples/rest/main.go#L176)
</details>
<details>
<summary>`/panic`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/panic**
- _GET_
- [main.main.func3](/_examples/rest/main.go#L77)
</details>
<details>
<summary>`/ping`</summary>
- [RequestID](/middleware/request_id.go#L63)
- [Logger](/middleware/logger.go#L26)
- [Recoverer](/middleware/recoverer.go#L18)
- [URLFormat](/middleware/url_format.go#L45)
- [SetContentType.func1](https://github.com/go-chi/render/content_type.go#L49)
- **/ping**
- _GET_
- [main.main.func2](/_examples/rest/main.go#L73)
</details>
Total # of routes: 10