From 24fd1656606a2554b8416ac4bffb66324b0dc743 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 3 Nov 2022 12:55:31 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20v3=20(feature):=20new=20mounting=20?= =?UTF-8?q?system=20(#2022)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * πŸ”₯ chore: removed mount from router * ✨ feat: new mounting system * ✨ feat: mount for group * βœ… test: updated and improve for new mount system * ♻️ refactor: better variable name * ♻️ refactor: better function naming * πŸ› fix: not mounted check * βœ… test: update onmount test if sub app is not mounted to parent * 🎨 perf: fix allocs problem * βœ… test: add benchmark for MountPath * ✨ feat: access to app's mountpath * βœ… test: remove parent app's mountpath test * remove some parts * add comment * fix Co-authored-by: Muhammed Efe Γ‡etin Co-authored-by: RW --- app.go | 35 ++++++++++++++++++++++++++--------- group.go | 36 +++++++++++++++++++++++++++--------- hooks_test.go | 10 +++++----- mount.go | 8 +++++--- mount_test.go | 38 +++++++++++++++++++------------------- router.go | 2 -- 6 files changed, 82 insertions(+), 47 deletions(-) diff --git a/app.go b/app.go index f8443079..1151e8fe 100644 --- a/app.go +++ b/app.go @@ -618,33 +618,50 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route { // Use registers a middleware route that will match requests // with the provided prefix (which is optional and defaults to "/"). +// Also, you can pass another app instance as a sub-router along a routing path. +// It's very useful to split up a large API as many independent routers and +// compose them as a single service using Use. The fiber's error handler and +// any of the fiber's sub apps are added to the application's error handlers +// to be invoked on errors that happen within the prefix route. // -// app.Use(func(c fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", func(c fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", handler, func(c fiber.Ctx) error { -// return c.Next() -// }) +// app.Use(func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", handler, func(c fiber.Ctx) error { +// return c.Next() +// }) +// subApp := fiber.New() +// app.Use("/mounted-path", subApp) // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...any) Router { var prefix string + var subApp *App var handlers []Handler for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case *App: + subApp = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } + + if subApp != nil { + app.mount(prefix, subApp) + return app + } + app.register([]string{methodUse}, prefix, nil, handlers...) + return app } diff --git a/group.go b/group.go index 0f6e95d1..e7becbeb 100644 --- a/group.go +++ b/group.go @@ -39,32 +39,50 @@ func (grp *Group) Name(name string) Router { // Use registers a middleware route that will match requests // with the provided prefix (which is optional and defaults to "/"). +// Also, you can pass another app instance as a sub-router along a routing path. +// It's very useful to split up a large API as many independent routers and +// compose them as a single service using Use. The fiber's error handler and +// any of the fiber's sub apps are added to the application's error handlers +// to be invoked on errors that happen within the prefix route. // -// app.Use(func(c fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", func(c fiber.Ctx) error { -// return c.Next() -// }) -// app.Use("/api", handler, func(c fiber.Ctx) error { -// return c.Next() -// }) +// app.Use(func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", func(c fiber.Ctx) error { +// return c.Next() +// }) +// app.Use("/api", handler, func(c fiber.Ctx) error { +// return c.Next() +// }) +// subApp := fiber.New() +// app.Use("/mounted-path", subApp) // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...any) Router { prefix := "" + var subApp *App var handlers []Handler + for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case *App: + subApp = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } + + if subApp != nil { + grp.mount(prefix, subApp) + return grp + } + grp.app.register([]string{methodUse}, getGroupPath(grp.Prefix, prefix), nil, handlers...) + return grp } diff --git a/hooks_test.go b/hooks_test.go index f3a23d7d..b4cb6420 100644 --- a/hooks_test.go +++ b/hooks_test.go @@ -30,7 +30,7 @@ func Test_Hook_OnRoute(t *testing.T) { subApp := New() subApp.Get("/test", testSimpleHandler) - app.Mount("/sub", subApp) + app.Use("/sub", subApp) } func Test_Hook_OnRoute_Mount(t *testing.T) { @@ -38,7 +38,7 @@ func Test_Hook_OnRoute_Mount(t *testing.T) { app := New() subApp := New() - app.Mount("/sub", subApp) + app.Use("/sub", subApp) subApp.Hooks().OnRoute(func(r Route) error { require.Equal(t, "/sub/test", r.Path) @@ -77,7 +77,7 @@ func Test_Hook_OnName(t *testing.T) { subApp.Get("/test", testSimpleHandler) subApp.Get("/test2", testSimpleHandler) - app.Mount("/sub", subApp) + app.Use("/sub", subApp) require.Equal(t, "index", buf.String()) } @@ -124,7 +124,7 @@ func Test_Hook_OnGroup_Mount(t *testing.T) { app := New() micro := New() - micro.Mount("/john", app) + micro.Use("/john", app) app.Hooks().OnGroup(func(g Group) error { require.Equal(t, "/john/v1", g.Prefix) @@ -255,5 +255,5 @@ func Test_Hook_OnMount(t *testing.T) { return nil }) - app.Mount("/sub", subApp) + app.Use("/sub", subApp) } diff --git a/mount.go b/mount.go index f7df3a97..ccb25eca 100644 --- a/mount.go +++ b/mount.go @@ -36,7 +36,7 @@ func newMountFields(app *App) *mountFields { // compose them as a single service using Mount. The fiber's error handler and // any of the fiber's sub apps are added to the application's error handlers // to be invoked on errors that happen within the prefix route. -func (app *App) Mount(prefix string, fiber *App) Router { +func (app *App) mount(prefix string, fiber *App) Router { prefix = strings.TrimRight(prefix, "/") if prefix == "" { prefix = "/" @@ -58,8 +58,10 @@ func (app *App) Mount(prefix string, fiber *App) Router { // Mount attaches another app instance as a sub-router along a routing path. // It's very useful to split up a large API as many independent routers and -// compose them as a single service using Mount. -func (grp *Group) Mount(prefix string, fiber *App) Router { +// compose them as a single service using Mount. The fiber's error handler and +// any of the fiber's sub apps are added to the application's error handlers +// to be invoked on errors that happen within the prefix route. +func (grp *Group) mount(prefix string, fiber *App) Router { groupPath := getGroupPath(grp.Prefix, prefix) groupPath = strings.TrimRight(groupPath, "/") if groupPath == "" { diff --git a/mount_test.go b/mount_test.go index 4e348932..a48f0376 100644 --- a/mount_test.go +++ b/mount_test.go @@ -21,7 +21,7 @@ func Test_App_Mount(t *testing.T) { }) app := New() - app.Mount("/john", micro) + app.Use("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/john/doe", nil)) require.Equal(t, nil, err, "app.Test(req)") require.Equal(t, 200, resp.StatusCode, "Status code") @@ -35,9 +35,9 @@ func Test_App_Mount_Nested(t *testing.T) { two := New() three := New() - two.Mount("/three", three) - app.Mount("/one", one) - one.Mount("/two", two) + two.Use("/three", three) + app.Use("/one", one) + one.Use("/two", two) one.Get("/doe", func(c Ctx) error { return c.SendStatus(StatusOK) @@ -73,9 +73,9 @@ func Test_App_MountPath(t *testing.T) { two := New() three := New() - two.Mount("/three", three) - one.Mount("/two", two) - app.Mount("/one", one) + two.Use("/three", three) + one.Use("/two", two) + app.Use("/one", one) require.Equal(t, "/one", one.MountPath()) require.Equal(t, "/one/two", two.MountPath()) @@ -96,7 +96,7 @@ func Test_App_ErrorHandler_GroupMount(t *testing.T) { app := New() v1 := app.Group("/v1") - v1.Mount("/john", micro) + v1.Use("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) testErrorResponse(t, err, resp, "1: custom error") @@ -115,7 +115,7 @@ func Test_App_ErrorHandler_GroupMountRootLevel(t *testing.T) { app := New() v1 := app.Group("/v1") - v1.Mount("/", micro) + v1.Use("/", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) testErrorResponse(t, err, resp, "1: custom error") @@ -130,7 +130,7 @@ func Test_App_Group_Mount(t *testing.T) { app := New() v1 := app.Group("/v1") - v1.Mount("/john", micro) + v1.Use("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) require.Equal(t, nil, err, "app.Test(req)") @@ -150,7 +150,7 @@ func Test_App_UseMountedErrorHandler(t *testing.T) { return errors.New("something happened") }) - app.Mount("/api", fiber) + app.Use("/api", fiber) resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) testErrorResponse(t, err, resp, "hi, i'm a custom error") @@ -168,7 +168,7 @@ func Test_App_UseMountedErrorHandlerRootLevel(t *testing.T) { return errors.New("something happened") }) - app.Mount("/", fiber) + app.Use("/", fiber) resp, err := app.Test(httptest.NewRequest(MethodGet, "/api", nil)) testErrorResponse(t, err, resp, "hi, i'm a custom error") @@ -196,7 +196,7 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { subfiber.Get("/", func(c Ctx) error { return errors.New("something happened") }) - subfiber.Mount("/third", tripleSubFiber) + subfiber.Use("/third", tripleSubFiber) f := func(c Ctx, err error) error { return c.Status(200).SendString("hi, i'm a custom error") @@ -207,9 +207,9 @@ func Test_App_UseMountedErrorHandlerForBestPrefixMatch(t *testing.T) { fiber.Get("/", func(c Ctx) error { return errors.New("something happened") }) - fiber.Mount("/sub", subfiber) + fiber.Use("/sub", subfiber) - app.Mount("/api", fiber) + app.Use("/api", fiber) resp, err := app.Test(httptest.NewRequest(MethodGet, "/api/sub", nil)) require.Equal(t, nil, err, "/api/sub req") @@ -246,7 +246,7 @@ func Test_Ctx_Render_Mount(t *testing.T) { }) app := New() - app.Mount("/hello", sub) + app.Use("/hello", sub) resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/a", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") @@ -301,8 +301,8 @@ func Test_Ctx_Render_Mount_ParentOrSubHasViews(t *testing.T) { return c.Render("bruh.tmpl", Map{}) }) - sub.Mount("/bruh", sub2) - app.Mount("/hello", sub) + sub.Use("/bruh", sub2) + app.Use("/hello", sub) resp, err := app.Test(httptest.NewRequest(MethodGet, "/hello/world/a", nil)) require.Equal(t, StatusOK, resp.StatusCode, "Status code") @@ -348,7 +348,7 @@ func Test_Ctx_Render_MountGroup(t *testing.T) { app := New() v1 := app.Group("/v1") - v1.Mount("/john", micro) + v1.Use("/john", micro) resp, err := app.Test(httptest.NewRequest(MethodGet, "/v1/john/doe", nil)) require.Equal(t, nil, err, "app.Test(req)") diff --git a/router.go b/router.go index a0a7e4e0..baab4682 100644 --- a/router.go +++ b/router.go @@ -38,8 +38,6 @@ type Router interface { Route(path string) Register - Mount(prefix string, fiber *App) Router - Name(name string) Router }