diff --git a/router_test.go b/router_test.go index 2147977c..584a2524 100644 --- a/router_test.go +++ b/router_test.go @@ -1,8 +1,718 @@ // ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ -// 📝 Github Repository: https://github.com/gofiber/fiber +// 🤖 Github Repository: https://github.com/gofiber/fiber // 📌 API Documentation: https://docs.gofiber.io -// ⚠️ This path parser was based on urlpath by @ucarion (MIT License). -// 💖 Modified for the Fiber router by @renanbastos93 & @renewerner87 -// 🤖 ucarion/urlpath - renanbastos93/fastpath - renewerner87/fastpath package fiber + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Handler -benchmem -count=3 + +import ( + "testing" + + utils "github.com/gofiber/utils" + fasthttp "github.com/valyala/fasthttp" +) + +func registerDummyRoutes(app *App) { + h := func(c *Ctx) {} + for _, r := range githubAPI { + switch r.method { + case "GET": + app.Get(r.path, h) + case "POST": + app.Post(r.path, h) + case "PUT": + app.Put(r.path, h) + case "PATCH": + app.Patch(r.path, h) + case "DELETE": + app.Delete(r.path, h) + default: + panic("Unknow HTTP method: " + r.method) + } + } +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Handler -benchmem -count=4 +func Benchmark_Router_Handler(b *testing.B) { + app := New() + registerDummyRoutes(app) + + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/user/keys/1337") + + for n := 0; n < b.N; n++ { + app.handler(c) + } +} + +func Benchmark_Router_Handler_Strict_Case(b *testing.B) { + app := New(&Settings{ + StrictRouting: true, + CaseSensitive: true, + }) + registerDummyRoutes(app) + + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/user/keys/1337") + + for n := 0; n < b.N; n++ { + app.handler(c) + } +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Chain -benchmem -count=4 +func Benchmark_Router_Chain(b *testing.B) { + app := New() + handler := func(c *Ctx) { + c.Next() + } + app.Get("/", handler, handler, handler, handler, handler, handler) + + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("GET") + c.URI().SetPath("/") + + for n := 0; n < b.N; n++ { + app.handler(c) + } +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Next -benchmem -count=4 +func Benchmark_Router_Next(b *testing.B) { + app := New() + registerDummyRoutes(app) + + request := &fasthttp.RequestCtx{} + + request.Request.Header.SetMethod("DELETE") + request.URI().SetPath("/user/keys/1337") + var res bool + + c := app.AcquireCtx(request) + defer app.ReleaseCtx(c) + + for n := 0; n < b.N; n++ { + c.index = -1 + res = app.next(c) + } + utils.AssertEqual(b, true, res) + utils.AssertEqual(b, 31, c.index) +} + +// go test -v ./... -run=^$ -bench=Benchmark_Route_Match -benchmem -count=4 +func Benchmark_Route_Match(b *testing.B) { + var match bool + var params []string + + parsed := parseRoute("/user/keys/:id") + route := &Route{ + use: false, + root: false, + star: false, + routeParser: parsed, + routeParams: parsed.params, + + Path: "/user/keys/:id", + Method: "DELETE", + } + route.Handlers = append(route.Handlers, func(c *Ctx) {}) + for n := 0; n < b.N; n++ { + match, params = route.match("/user/keys/1337", "/user/keys/1337") + } + + utils.AssertEqual(b, true, match) + utils.AssertEqual(b, []string{"1337"}, params) +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Handler_CaseSensitive -benchmem -count=4 +func Benchmark_Router_Handler_CaseSensitive(b *testing.B) { + app := New() + app.Settings.CaseSensitive = true + registerDummyRoutes(app) + + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/user/keys/1337") + + for n := 0; n < b.N; n++ { + app.handler(c) + } +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Handler_StrictRouting -benchmem -count=4 +func Benchmark_Router_Handler_StrictRouting(b *testing.B) { + app := New() + app.Settings.CaseSensitive = true + registerDummyRoutes(app) + + c := &fasthttp.RequestCtx{} + + c.Request.Header.SetMethod("DELETE") + c.URI().SetPath("/user/keys/1337") + + for n := 0; n < b.N; n++ { + app.handler(c) + } +} + +// go test -v ./... -run=^$ -bench=Benchmark_Router_Github_API -benchmem -count=4 +func Benchmark_Router_Github_API(b *testing.B) { + app := New() + registerDummyRoutes(app) + + var match bool + var params []string + + for n := 0; n < b.N; n++ { + for i := range testRoutes { + + mINT := methodINT[testRoutes[i].method] + path := testRoutes[i].path + + for i := range app.stack[mINT] { + match, params = app.stack[mINT][i].match(path, path) + } + } + } + + utils.AssertEqual(b, true, match) + utils.AssertEqual(b, true, params != nil) +} + +type testRoute struct { + method string + path string +} + +var testRoutes = []testRoute{ + // OAuth Authorizations + {"GET", "/authorizations"}, + {"GET", "/authorizations/1337"}, + {"POST", "/authorizations"}, + {"PUT", "/authorizations/clients/inf1nd873nf8912g9t"}, + {"PATCH", "/authorizations/1337"}, + {"DELETE", "/authorizations/1337"}, + {"GET", "/applications/2nds981mng6azl127y/tokens/sn108hbe1geheibf13f"}, + {"DELETE", "/applications/2nds981mng6azl127y/tokens"}, + {"DELETE", "/applications/2nds981mng6azl127y/tokens/sn108hbe1geheibf13f"}, + + // Activity + {"GET", "/events"}, + {"GET", "/repos/fenny/fiber/events"}, + {"GET", "/networks/fenny/fiber/events"}, + {"GET", "/orgs/gofiber/events"}, + {"GET", "/users/fenny/received_events"}, + {"GET", "/users/fenny/received_events/public"}, + {"GET", "/users/fenny/events"}, + {"GET", "/users/fenny/events/public"}, + {"GET", "/users/fenny/events/orgs/gofiber"}, + {"GET", "/feeds"}, + {"GET", "/notifications"}, + {"GET", "/repos/fenny/fiber/notifications"}, + {"PUT", "/notifications"}, + {"PUT", "/repos/fenny/fiber/notifications"}, + {"GET", "/notifications/threads/1337"}, + {"PATCH", "/notifications/threads/1337"}, + {"GET", "/notifications/threads/1337/subscription"}, + {"PUT", "/notifications/threads/1337/subscription"}, + {"DELETE", "/notifications/threads/1337/subscription"}, + {"GET", "/repos/fenny/fiber/stargazers"}, + {"GET", "/users/fenny/starred"}, + {"GET", "/user/starred"}, + {"GET", "/user/starred/fenny/fiber"}, + {"PUT", "/user/starred/fenny/fiber"}, + {"DELETE", "/user/starred/fenny/fiber"}, + {"GET", "/repos/fenny/fiber/subscribers"}, + {"GET", "/users/fenny/subscriptions"}, + {"GET", "/user/subscriptions"}, + {"GET", "/repos/fenny/fiber/subscription"}, + {"PUT", "/repos/fenny/fiber/subscription"}, + {"DELETE", "/repos/fenny/fiber/subscription"}, + {"GET", "/user/subscriptions/fenny/fiber"}, + {"PUT", "/user/subscriptions/fenny/fiber"}, + {"DELETE", "/user/subscriptions/fenny/fiber"}, + + // Gists + {"GET", "/users/fenny/gists"}, + {"GET", "/gists"}, + {"GET", "/gists/public"}, + {"GET", "/gists/starred"}, + {"GET", "/gists/1337"}, + {"POST", "/gists"}, + {"PATCH", "/gists/1337"}, + {"PUT", "/gists/1337/star"}, + {"DELETE", "/gists/1337/star"}, + {"GET", "/gists/1337/star"}, + {"POST", "/gists/1337/forks"}, + {"DELETE", "/gists/1337"}, + + // Git Data + {"GET", "/repos/fenny/fiber/git/blobs/v948b24g98ubngw9082bn02giub"}, + {"POST", "/repos/fenny/fiber/git/blobs"}, + {"GET", "/repos/fenny/fiber/git/commits/v948b24g98ubngw9082bn02giub"}, + {"POST", "/repos/fenny/fiber/git/commits"}, + {"GET", "/repos/fenny/fiber/git/refs/im/a/wildcard"}, + {"GET", "/repos/fenny/fiber/git/refs"}, + {"POST", "/repos/fenny/fiber/git/refs"}, + {"PATCH", "/repos/fenny/fiber/git/refs/im/a/wildcard"}, + {"DELETE", "/repos/fenny/fiber/git/refs/im/a/wildcard"}, + {"GET", "/repos/fenny/fiber/git/tags/v948b24g98ubngw9082bn02giub"}, + {"POST", "/repos/fenny/fiber/git/tags"}, + {"GET", "/repos/fenny/fiber/git/trees/v948b24g98ubngw9082bn02giub"}, + {"POST", "/repos/fenny/fiber/git/trees"}, + + // Issues + {"GET", "/issues"}, + {"GET", "/user/issues"}, + {"GET", "/orgs/gofiber/issues"}, + {"GET", "/repos/fenny/fiber/issues"}, + {"GET", "/repos/fenny/fiber/issues/1000"}, + {"POST", "/repos/fenny/fiber/issues"}, + {"PATCH", "/repos/fenny/fiber/issues/1000"}, + {"GET", "/repos/fenny/fiber/assignees"}, + {"GET", "/repos/fenny/fiber/assignees/nic"}, + {"GET", "/repos/fenny/fiber/issues/1000/comments"}, + {"GET", "/repos/fenny/fiber/issues/comments"}, + {"GET", "/repos/fenny/fiber/issues/comments/1337"}, + {"POST", "/repos/fenny/fiber/issues/1000/comments"}, + {"PATCH", "/repos/fenny/fiber/issues/comments/1337"}, + {"DELETE", "/repos/fenny/fiber/issues/comments/1337"}, + {"GET", "/repos/fenny/fiber/issues/1000/events"}, + {"GET", "/repos/fenny/fiber/issues/events"}, + {"GET", "/repos/fenny/fiber/issues/events/1337"}, + {"GET", "/repos/fenny/fiber/labels"}, + {"GET", "/repos/fenny/fiber/labels/john"}, + {"POST", "/repos/fenny/fiber/labels"}, + {"PATCH", "/repos/fenny/fiber/labels/john"}, + {"DELETE", "/repos/fenny/fiber/labels/john"}, + {"GET", "/repos/fenny/fiber/issues/1000/labels"}, + {"POST", "/repos/fenny/fiber/issues/1000/labels"}, + {"DELETE", "/repos/fenny/fiber/issues/1000/labels/john"}, + {"PUT", "/repos/fenny/fiber/issues/1000/labels"}, + {"DELETE", "/repos/fenny/fiber/issues/1000/labels"}, + {"GET", "/repos/fenny/fiber/milestones/1000/labels"}, + {"GET", "/repos/fenny/fiber/milestones"}, + {"GET", "/repos/fenny/fiber/milestones/1000"}, + {"POST", "/repos/fenny/fiber/milestones"}, + {"PATCH", "/repos/fenny/fiber/milestones/1000"}, + {"DELETE", "/repos/fenny/fiber/milestones/1000"}, + + // Miscellaneous + {"GET", "/emojis"}, + {"GET", "/gitignore/templates"}, + {"GET", "/gitignore/templates/john"}, + {"POST", "/markdown"}, + {"POST", "/markdown/raw"}, + {"GET", "/meta"}, + {"GET", "/rate_limit"}, + + // Organizations + {"GET", "/users/fenny/orgs"}, + {"GET", "/user/orgs"}, + {"GET", "/orgs/gofiber"}, + {"PATCH", "/orgs/gofiber"}, + {"GET", "/orgs/gofiber/members"}, + {"GET", "/orgs/gofiber/members/fenny"}, + {"DELETE", "/orgs/gofiber/members/fenny"}, + {"GET", "/orgs/gofiber/public_members"}, + {"GET", "/orgs/gofiber/public_members/fenny"}, + {"PUT", "/orgs/gofiber/public_members/fenny"}, + {"DELETE", "/orgs/gofiber/public_members/fenny"}, + {"GET", "/orgs/gofiber/teams"}, + {"GET", "/teams/1337"}, + {"POST", "/orgs/gofiber/teams"}, + {"PATCH", "/teams/1337"}, + {"DELETE", "/teams/1337"}, + {"GET", "/teams/1337/members"}, + {"GET", "/teams/1337/members/fenny"}, + {"PUT", "/teams/1337/members/fenny"}, + {"DELETE", "/teams/1337/members/fenny"}, + {"GET", "/teams/1337/repos"}, + {"GET", "/teams/1337/repos/fenny/fiber"}, + {"PUT", "/teams/1337/repos/fenny/fiber"}, + {"DELETE", "/teams/1337/repos/fenny/fiber"}, + {"GET", "/user/teams"}, + + // Pull Requests + {"GET", "/repos/fenny/fiber/pulls"}, + {"GET", "/repos/fenny/fiber/pulls/1000"}, + {"POST", "/repos/fenny/fiber/pulls"}, + {"PATCH", "/repos/fenny/fiber/pulls/1000"}, + {"GET", "/repos/fenny/fiber/pulls/1000/commits"}, + {"GET", "/repos/fenny/fiber/pulls/1000/files"}, + {"GET", "/repos/fenny/fiber/pulls/1000/merge"}, + {"PUT", "/repos/fenny/fiber/pulls/1000/merge"}, + {"GET", "/repos/fenny/fiber/pulls/1000/comments"}, + {"GET", "/repos/fenny/fiber/pulls/comments"}, + {"GET", "/repos/fenny/fiber/pulls/comments/1000"}, + {"PUT", "/repos/fenny/fiber/pulls/1000/comments"}, + {"PATCH", "/repos/fenny/fiber/pulls/comments/1000"}, + {"DELETE", "/repos/fenny/fiber/pulls/comments/1000"}, + + // Repositories + {"GET", "/user/repos"}, + {"GET", "/users/fenny/repos"}, + {"GET", "/orgs/gofiber/repos"}, + {"GET", "/repositories"}, + {"POST", "/user/repos"}, + {"POST", "/orgs/gofiber/repos"}, + {"GET", "/repos/fenny/fiber"}, + {"PATCH", "/repos/fenny/fiber"}, + {"GET", "/repos/fenny/fiber/contributors"}, + {"GET", "/repos/fenny/fiber/languages"}, + {"GET", "/repos/fenny/fiber/teams"}, + {"GET", "/repos/fenny/fiber/tags"}, + {"GET", "/repos/fenny/fiber/branches"}, + {"GET", "/repos/fenny/fiber/branches/master"}, + {"DELETE", "/repos/fenny/fiber"}, + {"GET", "/repos/fenny/fiber/collaborators"}, + {"GET", "/repos/fenny/fiber/collaborators/fenny"}, + {"PUT", "/repos/fenny/fiber/collaborators/fenny"}, + {"DELETE", "/repos/fenny/fiber/collaborators/fenny"}, + {"GET", "/repos/fenny/fiber/comments"}, + {"GET", "/repos/fenny/fiber/commits/v948b24g98ubngw9082bn02giub/comments"}, + {"POST", "/repos/fenny/fiber/commits/v948b24g98ubngw9082bn02giub/comments"}, + {"GET", "/repos/fenny/fiber/comments/1337"}, + {"PATCH", "/repos/fenny/fiber/comments/1337"}, + {"DELETE", "/repos/fenny/fiber/comments/1337"}, + {"GET", "/repos/fenny/fiber/commits"}, + {"GET", "/repos/fenny/fiber/commits/v948b24g98ubngw9082bn02giub"}, + {"GET", "/repos/fenny/fiber/readme"}, + {"GET", "/repos/fenny/fiber/contents/im/a/wildcard"}, + {"PUT", "/repos/fenny/fiber/contents/im/a/wildcard"}, + {"DELETE", "/repos/fenny/fiber/contents/im/a/wildcard"}, + {"GET", "/repos/fenny/fiber/gzip/google"}, + {"GET", "/repos/fenny/fiber/keys"}, + {"GET", "/repos/fenny/fiber/keys/1337"}, + {"POST", "/repos/fenny/fiber/keys"}, + {"PATCH", "/repos/fenny/fiber/keys/1337"}, + {"DELETE", "/repos/fenny/fiber/keys/1337"}, + {"GET", "/repos/fenny/fiber/downloads"}, + {"GET", "/repos/fenny/fiber/downloads/1337"}, + {"DELETE", "/repos/fenny/fiber/downloads/1337"}, + {"GET", "/repos/fenny/fiber/forks"}, + {"POST", "/repos/fenny/fiber/forks"}, + {"GET", "/repos/fenny/fiber/hooks"}, + {"GET", "/repos/fenny/fiber/hooks/1337"}, + {"POST", "/repos/fenny/fiber/hooks"}, + {"PATCH", "/repos/fenny/fiber/hooks/1337"}, + {"POST", "/repos/fenny/fiber/hooks/1337/tests"}, + {"DELETE", "/repos/fenny/fiber/hooks/1337"}, + {"POST", "/repos/fenny/fiber/merges"}, + {"GET", "/repos/fenny/fiber/releases"}, + {"GET", "/repos/fenny/fiber/releases/1337"}, + {"POST", "/repos/fenny/fiber/releases"}, + {"PATCH", "/repos/fenny/fiber/releases/1337"}, + {"DELETE", "/repos/fenny/fiber/releases/1337"}, + {"GET", "/repos/fenny/fiber/releases/1337/assets"}, + {"GET", "/repos/fenny/fiber/stats/contributors"}, + {"GET", "/repos/fenny/fiber/stats/commit_activity"}, + {"GET", "/repos/fenny/fiber/stats/code_frequency"}, + {"GET", "/repos/fenny/fiber/stats/participation"}, + {"GET", "/repos/fenny/fiber/stats/punch_card"}, + {"GET", "/repos/fenny/fiber/statuses/google"}, + {"POST", "/repos/fenny/fiber/statuses/google"}, + + // Search + {"GET", "/search/repositories"}, + {"GET", "/search/code"}, + {"GET", "/search/issues"}, + {"GET", "/search/users"}, + {"GET", "/legacy/issues/search/fenny/fibersitory/locked/finish"}, + {"GET", "/legacy/repos/search/finish"}, + {"GET", "/legacy/user/search/finish"}, + {"GET", "/legacy/user/email/info@gofiber.io"}, + + // Users + {"GET", "/users/fenny"}, + {"GET", "/user"}, + {"PATCH", "/user"}, + {"GET", "/users"}, + {"GET", "/user/emails"}, + {"POST", "/user/emails"}, + {"DELETE", "/user/emails"}, + {"GET", "/users/fenny/followers"}, + {"GET", "/user/followers"}, + {"GET", "/users/fenny/following"}, + {"GET", "/user/following"}, + {"GET", "/user/following/fenny"}, + {"GET", "/users/fenny/following/renan"}, + {"PUT", "/user/following/fenny"}, + {"DELETE", "/user/following/fenny"}, + {"GET", "/users/fenny/keys"}, + {"GET", "/user/keys"}, + {"GET", "/user/keys/1337"}, + {"POST", "/user/keys"}, + {"PATCH", "/user/keys/1337"}, + {"DELETE", "/user/keys/1337"}, +} + +var githubAPI = []testRoute{ + // OAuth Authorizations + {"GET", "/authorizations"}, + {"GET", "/authorizations/:id"}, + {"POST", "/authorizations"}, + {"PUT", "/authorizations/clients/:client_id"}, + {"PATCH", "/authorizations/:id"}, + {"DELETE", "/authorizations/:id"}, + {"GET", "/applications/:client_id/tokens/:access_token"}, + {"DELETE", "/applications/:client_id/tokens"}, + {"DELETE", "/applications/:client_id/tokens/:access_token"}, + + // Activity + {"GET", "/events"}, + {"GET", "/repos/:owner/:repo/events"}, + {"GET", "/networks/:owner/:repo/events"}, + {"GET", "/orgs/:org/events"}, + {"GET", "/users/:user/received_events"}, + {"GET", "/users/:user/received_events/public"}, + {"GET", "/users/:user/events"}, + {"GET", "/users/:user/events/public"}, + {"GET", "/users/:user/events/orgs/:org"}, + {"GET", "/feeds"}, + {"GET", "/notifications"}, + {"GET", "/repos/:owner/:repo/notifications"}, + {"PUT", "/notifications"}, + {"PUT", "/repos/:owner/:repo/notifications"}, + {"GET", "/notifications/threads/:id"}, + {"PATCH", "/notifications/threads/:id"}, + {"GET", "/notifications/threads/:id/subscription"}, + {"PUT", "/notifications/threads/:id/subscription"}, + {"DELETE", "/notifications/threads/:id/subscription"}, + {"GET", "/repos/:owner/:repo/stargazers"}, + {"GET", "/users/:user/starred"}, + {"GET", "/user/starred"}, + {"GET", "/user/starred/:owner/:repo"}, + {"PUT", "/user/starred/:owner/:repo"}, + {"DELETE", "/user/starred/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/subscribers"}, + {"GET", "/users/:user/subscriptions"}, + {"GET", "/user/subscriptions"}, + {"GET", "/repos/:owner/:repo/subscription"}, + {"PUT", "/repos/:owner/:repo/subscription"}, + {"DELETE", "/repos/:owner/:repo/subscription"}, + {"GET", "/user/subscriptions/:owner/:repo"}, + {"PUT", "/user/subscriptions/:owner/:repo"}, + {"DELETE", "/user/subscriptions/:owner/:repo"}, + + // Gists + {"GET", "/users/:user/gists"}, + {"GET", "/gists"}, + {"GET", "/gists/public"}, + {"GET", "/gists/starred"}, + {"GET", "/gists/:id"}, + {"POST", "/gists"}, + {"PATCH", "/gists/:id"}, + {"PUT", "/gists/:id/star"}, + {"DELETE", "/gists/:id/star"}, + {"GET", "/gists/:id/star"}, + {"POST", "/gists/:id/forks"}, + {"DELETE", "/gists/:id"}, + + // Git Data + {"GET", "/repos/:owner/:repo/git/blobs/:sha"}, + {"POST", "/repos/:owner/:repo/git/blobs"}, + {"GET", "/repos/:owner/:repo/git/commits/:sha"}, + {"POST", "/repos/:owner/:repo/git/commits"}, + {"GET", "/repos/:owner/:repo/git/refs/*"}, + {"GET", "/repos/:owner/:repo/git/refs"}, + {"POST", "/repos/:owner/:repo/git/refs"}, + {"PATCH", "/repos/:owner/:repo/git/refs/*"}, + {"DELETE", "/repos/:owner/:repo/git/refs/*"}, + {"GET", "/repos/:owner/:repo/git/tags/:sha"}, + {"POST", "/repos/:owner/:repo/git/tags"}, + {"GET", "/repos/:owner/:repo/git/trees/:sha"}, + {"POST", "/repos/:owner/:repo/git/trees"}, + + // Issues + {"GET", "/issues"}, + {"GET", "/user/issues"}, + {"GET", "/orgs/:org/issues"}, + {"GET", "/repos/:owner/:repo/issues"}, + {"GET", "/repos/:owner/:repo/issues/:number"}, + {"POST", "/repos/:owner/:repo/issues"}, + {"PATCH", "/repos/:owner/:repo/issues/:number"}, + {"GET", "/repos/:owner/:repo/assignees"}, + {"GET", "/repos/:owner/:repo/assignees/:assignee"}, + {"GET", "/repos/:owner/:repo/issues/:number/comments"}, + {"GET", "/repos/:owner/:repo/issues/comments"}, + {"GET", "/repos/:owner/:repo/issues/comments/:id"}, + {"POST", "/repos/:owner/:repo/issues/:number/comments"}, + {"PATCH", "/repos/:owner/:repo/issues/comments/:id"}, + {"DELETE", "/repos/:owner/:repo/issues/comments/:id"}, + {"GET", "/repos/:owner/:repo/issues/:number/events"}, + {"GET", "/repos/:owner/:repo/issues/events"}, + {"GET", "/repos/:owner/:repo/issues/events/:id"}, + {"GET", "/repos/:owner/:repo/labels"}, + {"GET", "/repos/:owner/:repo/labels/:name"}, + {"POST", "/repos/:owner/:repo/labels"}, + {"PATCH", "/repos/:owner/:repo/labels/:name"}, + {"DELETE", "/repos/:owner/:repo/labels/:name"}, + {"GET", "/repos/:owner/:repo/issues/:number/labels"}, + {"POST", "/repos/:owner/:repo/issues/:number/labels"}, + {"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"}, + {"PUT", "/repos/:owner/:repo/issues/:number/labels"}, + {"DELETE", "/repos/:owner/:repo/issues/:number/labels"}, + {"GET", "/repos/:owner/:repo/milestones/:number/labels"}, + {"GET", "/repos/:owner/:repo/milestones"}, + {"GET", "/repos/:owner/:repo/milestones/:number"}, + {"POST", "/repos/:owner/:repo/milestones"}, + {"PATCH", "/repos/:owner/:repo/milestones/:number"}, + {"DELETE", "/repos/:owner/:repo/milestones/:number"}, + + // Miscellaneous + {"GET", "/emojis"}, + {"GET", "/gitignore/templates"}, + {"GET", "/gitignore/templates/:name"}, + {"POST", "/markdown"}, + {"POST", "/markdown/raw"}, + {"GET", "/meta"}, + {"GET", "/rate_limit"}, + + // Organizations + {"GET", "/users/:user/orgs"}, + {"GET", "/user/orgs"}, + {"GET", "/orgs/:org"}, + {"PATCH", "/orgs/:org"}, + {"GET", "/orgs/:org/members"}, + {"GET", "/orgs/:org/members/:user"}, + {"DELETE", "/orgs/:org/members/:user"}, + {"GET", "/orgs/:org/public_members"}, + {"GET", "/orgs/:org/public_members/:user"}, + {"PUT", "/orgs/:org/public_members/:user"}, + {"DELETE", "/orgs/:org/public_members/:user"}, + {"GET", "/orgs/:org/teams"}, + {"GET", "/teams/:id"}, + {"POST", "/orgs/:org/teams"}, + {"PATCH", "/teams/:id"}, + {"DELETE", "/teams/:id"}, + {"GET", "/teams/:id/members"}, + {"GET", "/teams/:id/members/:user"}, + {"PUT", "/teams/:id/members/:user"}, + {"DELETE", "/teams/:id/members/:user"}, + {"GET", "/teams/:id/repos"}, + {"GET", "/teams/:id/repos/:owner/:repo"}, + {"PUT", "/teams/:id/repos/:owner/:repo"}, + {"DELETE", "/teams/:id/repos/:owner/:repo"}, + {"GET", "/user/teams"}, + + // Pull Requests + {"GET", "/repos/:owner/:repo/pulls"}, + {"GET", "/repos/:owner/:repo/pulls/:number"}, + {"POST", "/repos/:owner/:repo/pulls"}, + {"PATCH", "/repos/:owner/:repo/pulls/:number"}, + {"GET", "/repos/:owner/:repo/pulls/:number/commits"}, + {"GET", "/repos/:owner/:repo/pulls/:number/files"}, + {"GET", "/repos/:owner/:repo/pulls/:number/merge"}, + {"PUT", "/repos/:owner/:repo/pulls/:number/merge"}, + {"GET", "/repos/:owner/:repo/pulls/:number/comments"}, + {"GET", "/repos/:owner/:repo/pulls/comments"}, + {"GET", "/repos/:owner/:repo/pulls/comments/:number"}, + {"PUT", "/repos/:owner/:repo/pulls/:number/comments"}, + {"PATCH", "/repos/:owner/:repo/pulls/comments/:number"}, + {"DELETE", "/repos/:owner/:repo/pulls/comments/:number"}, + + // Repositories + {"GET", "/user/repos"}, + {"GET", "/users/:user/repos"}, + {"GET", "/orgs/:org/repos"}, + {"GET", "/repositories"}, + {"POST", "/user/repos"}, + {"POST", "/orgs/:org/repos"}, + {"GET", "/repos/:owner/:repo"}, + {"PATCH", "/repos/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/contributors"}, + {"GET", "/repos/:owner/:repo/languages"}, + {"GET", "/repos/:owner/:repo/teams"}, + {"GET", "/repos/:owner/:repo/tags"}, + {"GET", "/repos/:owner/:repo/branches"}, + {"GET", "/repos/:owner/:repo/branches/:branch"}, + {"DELETE", "/repos/:owner/:repo"}, + {"GET", "/repos/:owner/:repo/collaborators"}, + {"GET", "/repos/:owner/:repo/collaborators/:user"}, + {"PUT", "/repos/:owner/:repo/collaborators/:user"}, + {"DELETE", "/repos/:owner/:repo/collaborators/:user"}, + {"GET", "/repos/:owner/:repo/comments"}, + {"GET", "/repos/:owner/:repo/commits/:sha/comments"}, + {"POST", "/repos/:owner/:repo/commits/:sha/comments"}, + {"GET", "/repos/:owner/:repo/comments/:id"}, + {"PATCH", "/repos/:owner/:repo/comments/:id"}, + {"DELETE", "/repos/:owner/:repo/comments/:id"}, + {"GET", "/repos/:owner/:repo/commits"}, + {"GET", "/repos/:owner/:repo/commits/:sha"}, + {"GET", "/repos/:owner/:repo/readme"}, + {"GET", "/repos/:owner/:repo/contents/*"}, + {"PUT", "/repos/:owner/:repo/contents/*"}, + {"DELETE", "/repos/:owner/:repo/contents/*"}, + {"GET", "/repos/:owner/:repo/:archive_format/:ref"}, + {"GET", "/repos/:owner/:repo/keys"}, + {"GET", "/repos/:owner/:repo/keys/:id"}, + {"POST", "/repos/:owner/:repo/keys"}, + {"PATCH", "/repos/:owner/:repo/keys/:id"}, + {"DELETE", "/repos/:owner/:repo/keys/:id"}, + {"GET", "/repos/:owner/:repo/downloads"}, + {"GET", "/repos/:owner/:repo/downloads/:id"}, + {"DELETE", "/repos/:owner/:repo/downloads/:id"}, + {"GET", "/repos/:owner/:repo/forks"}, + {"POST", "/repos/:owner/:repo/forks"}, + {"GET", "/repos/:owner/:repo/hooks"}, + {"GET", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/hooks"}, + {"PATCH", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/hooks/:id/tests"}, + {"DELETE", "/repos/:owner/:repo/hooks/:id"}, + {"POST", "/repos/:owner/:repo/merges"}, + {"GET", "/repos/:owner/:repo/releases"}, + {"GET", "/repos/:owner/:repo/releases/:id"}, + {"POST", "/repos/:owner/:repo/releases"}, + {"PATCH", "/repos/:owner/:repo/releases/:id"}, + {"DELETE", "/repos/:owner/:repo/releases/:id"}, + {"GET", "/repos/:owner/:repo/releases/:id/assets"}, + {"GET", "/repos/:owner/:repo/stats/contributors"}, + {"GET", "/repos/:owner/:repo/stats/commit_activity"}, + {"GET", "/repos/:owner/:repo/stats/code_frequency"}, + {"GET", "/repos/:owner/:repo/stats/participation"}, + {"GET", "/repos/:owner/:repo/stats/punch_card"}, + {"GET", "/repos/:owner/:repo/statuses/:ref"}, + {"POST", "/repos/:owner/:repo/statuses/:ref"}, + + // Search + {"GET", "/search/repositories"}, + {"GET", "/search/code"}, + {"GET", "/search/issues"}, + {"GET", "/search/users"}, + {"GET", "/legacy/issues/search/:owner/:repository/:state/:keyword"}, + {"GET", "/legacy/repos/search/:keyword"}, + {"GET", "/legacy/user/search/:keyword"}, + {"GET", "/legacy/user/email/:email"}, + + // Users + {"GET", "/users/:user"}, + {"GET", "/user"}, + {"PATCH", "/user"}, + {"GET", "/users"}, + {"GET", "/user/emails"}, + {"POST", "/user/emails"}, + {"DELETE", "/user/emails"}, + {"GET", "/users/:user/followers"}, + {"GET", "/user/followers"}, + {"GET", "/users/:user/following"}, + {"GET", "/user/following"}, + {"GET", "/user/following/:user"}, + {"GET", "/users/:user/following/:target_user"}, + {"PUT", "/user/following/:user"}, + {"DELETE", "/user/following/:user"}, + {"GET", "/users/:user/keys"}, + {"GET", "/user/keys"}, + {"GET", "/user/keys/:id"}, + {"POST", "/user/keys"}, + {"PATCH", "/user/keys/:id"}, + {"DELETE", "/user/keys/:id"}, +}