From 375412b7dd304b481ddc607812d3bad7ebf02337 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 16:33:02 +0300 Subject: [PATCH 01/15] =?UTF-8?q?=F0=9F=94=A5=20chore:=20removed=20mount?= =?UTF-8?q?=20from=20router?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- router.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/router.go b/router.go index cf048b35c2..3c406271dd 100644 --- a/router.go +++ b/router.go @@ -38,8 +38,6 @@ type Router interface { Route(prefix string, fn func(router Router), name ...string) Router - Mount(prefix string, fiber *App) Router - Name(name string) Router } From bb17cb8a2c260466f698191d76b491a3a4cd0649 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 16:47:17 +0300 Subject: [PATCH 02/15] =?UTF-8?q?=E2=9C=A8=20feat:=20new=20mounting=20syst?= =?UTF-8?q?em?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 95 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 30 deletions(-) diff --git a/app.go b/app.go index 405a4296f7..03f4ec5d21 100644 --- a/app.go +++ b/app.go @@ -116,6 +116,10 @@ type App struct { getString func(b []byte) string // Mounted and main apps appList map[string]*App + // If application is a parent, It returns nil. It can accessible only from sub app + parent *App + // Mounted sub app's path + mountpath string // Hooks hooks *hooks // Latest route & group @@ -465,6 +469,8 @@ func New(config ...Config) *App { getBytes: utils.UnsafeBytes, getString: utils.UnsafeString, appList: make(map[string]*App), + parent: nil, + mountpath: "", latestRoute: &Route{}, latestGroup: &Group{}, customBinders: []CustomBinder{}, @@ -561,36 +567,6 @@ func (app *App) RegisterCustomBinder(binder CustomBinder) { app.customBinders = append(app.customBinders, binder) } -// 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. 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 { - stack := fiber.Stack() - prefix = strings.TrimRight(prefix, "/") - if prefix == "" { - prefix = "/" - } - - for m := range stack { - for r := range stack[m] { - route := app.copyRoute(stack[m][r]) - app.addRoute(route.Method, app.addPrefixToRoute(prefix, route)) - } - } - - // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.appList { - app.appList[prefix+mountedPrefixes] = subApp - subApp.init() - } - - atomic.AddUint32(&app.handlersCount, fiber.handlersCount) - - return app -} - // Assign name to specific route. func (app *App) Name(name string) Router { app.mutex.Lock() @@ -637,18 +613,27 @@ func (app *App) GetRoute(name string) Route { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...any) Router { var prefix string + var mountedApp *App var handlers []Handler for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case *App: + mountedApp = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } + + if mountedApp != nil { + app.mount(prefix, mountedApp) + return app + } + app.register(methodUse, prefix, handlers...) return app } @@ -724,6 +709,25 @@ func (app *App) All(path string, handlers ...Handler) Router { return app } +// The MountPath property contains one or more path patterns on which a sub-app was mounted. +func (app *App) Mountpath() string { + if app.mountpath == "" { + panic("mountpath cannot be used on parent app") + } + + return utils.CopyString(app.mountpath) +} + +// The mount event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. +func (app *App) OnMount(callback func(parent *App)) { + if app.mountpath == "" { + panic("onmount cannot be used on parent app") + } + + // returns parent app in callback + callback(app.parent) +} + // Group is used for Routes with common prefix to define a new sub-router with optional middleware. // // api := app.Group("/api") @@ -1116,6 +1120,37 @@ func (app *App) ErrorHandler(ctx Ctx, err error) error { return app.config.ErrorHandler(ctx, err) } +// 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. 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, sub *App) *App { + stack := sub.Stack() + prefix = strings.TrimRight(prefix, "/") + if prefix == "" { + prefix = "/" + } + + for m := range stack { + for r := range stack[m] { + route := app.copyRoute(stack[m][r]) + app.addRoute(route.Method, app.addPrefixToRoute(prefix, route)) + } + } + + // Support for configs of mounted-apps and sub-mounted-apps + for mountedPrefixes, subApp := range sub.appList { + app.appList[prefix+mountedPrefixes] = subApp + subApp.parent = app + subApp.mountpath = prefix + mountedPrefixes + subApp.init() + } + + atomic.AddUint32(&app.handlersCount, sub.handlersCount) + return app +} + // serverErrorHandler is a wrapper around the application's error handler method // user for the fasthttp server configuration. It maps a set of fasthttp errors to fiber // errors before calling the application's error handler method. From 780f06aff36c1aeae8d6bc2e7e2daa33a5d3b90c Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 16:47:28 +0300 Subject: [PATCH 03/15] =?UTF-8?q?=E2=9C=A8=20feat:=20mount=20for=20group?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- group.go | 45 +++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/group.go b/group.go index 612012939b..88771ead25 100644 --- a/group.go +++ b/group.go @@ -22,29 +22,28 @@ type Group struct { // 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 { - stack := fiber.Stack() +func (grp *Group) mount(prefix string, sub *App) Router { + stack := sub.Stack() groupPath := getGroupPath(grp.Prefix, prefix) groupPath = strings.TrimRight(groupPath, "/") if groupPath == "" { groupPath = "/" } - for m := range stack { for r := range stack[m] { route := grp.app.copyRoute(stack[m][r]) grp.app.addRoute(route.Method, grp.app.addPrefixToRoute(groupPath, route)) } } - // Support for configs of mounted-apps and sub-mounted-apps - for mountedPrefixes, subApp := range fiber.appList { + for mountedPrefixes, subApp := range sub.appList { grp.app.appList[groupPath+mountedPrefixes] = subApp + subApp.parent = grp.app + subApp.mountpath = groupPath + mountedPrefixes subApp.init() } - atomic.AddUint32(&grp.app.handlersCount, fiber.handlersCount) - + atomic.AddUint32(&grp.app.handlersCount, sub.handlersCount) return grp } @@ -70,30 +69,39 @@ 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 "/"). // -// 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() +// }) // // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...any) Router { prefix := "" + var mountedApp *App var handlers []Handler for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg + case *App: + mountedApp = arg case Handler: handlers = append(handlers, arg) default: panic(fmt.Sprintf("use: invalid handler %v\n", reflect.TypeOf(arg))) } } + + if mountedApp != nil { + grp.mount(prefix, mountedApp) + return grp + } + grp.app.register(methodUse, getGroupPath(grp.Prefix, prefix), handlers...) return grp } @@ -171,8 +179,9 @@ func (grp *Group) All(path string, handlers ...Handler) Router { } // Group is used for Routes with common prefix to define a new sub-router with optional middleware. -// api := app.Group("/api") -// api.Get("/users", handler) +// +// api := app.Group("/api") +// api.Get("/users", handler) func (grp *Group) Group(prefix string, handlers ...Handler) Router { prefix = getGroupPath(grp.Prefix, prefix) if len(handlers) > 0 { From e409d80c39e47e58fbbfb917134d8e766237c9f5 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 16:48:02 +0300 Subject: [PATCH 04/15] =?UTF-8?q?=E2=9C=85=20test:=20updated=20and=20impro?= =?UTF-8?q?ve=20for=20new=20mount=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++-------- ctx_test.go | 4 ++-- hooks_test.go | 4 ++-- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/app_test.go b/app_test.go index a67e7085d5..1cdd3948e5 100644 --- a/app_test.go +++ b/app_test.go @@ -261,7 +261,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") @@ -280,7 +280,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") @@ -317,7 +317,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)) utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -325,6 +325,48 @@ func Test_App_Mount(t *testing.T) { utils.AssertEqual(t, uint32(2), app.handlersCount) } +func Test_App_Mountpath(t *testing.T) { + parent := New() + sub := New() + sub1 := New() + + parent.Use("/sub", sub) + parent.Use("/sub1", sub1) + + utils.AssertEqual(t, "/sub", sub.Mountpath()) + utils.AssertEqual(t, "/sub1", sub1.Mountpath()) + + defer func() { + if err := recover(); err != nil { + utils.AssertEqual(t, "mountpath cannot be used on parent app", fmt.Sprintf("%s", err)) + } + }() + parent.Mountpath() +} + +func Test_App_OnMount(t *testing.T) { + app := New() + sub := New() + + app.Use("/sub", sub) + + sub.OnMount(func(parent *App) { + //Check parent app + utils.AssertEqual(t, app.mountpath, parent.mountpath) + }) + + sub.OnMount(func(parent *App) { + utils.AssertEqual(t, parent != nil, true) + }) + + defer func() { + if err := recover(); err != nil { + utils.AssertEqual(t, "onmount cannot be used on parent app", fmt.Sprintf("%s", err)) + } + }() + app.OnMount(func(parent *App) {}) +} + func Test_App_Use_Params(t *testing.T) { app := New() @@ -966,7 +1008,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)) utils.AssertEqual(t, nil, err, "app.Test(req)") @@ -1615,7 +1657,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") @@ -1633,7 +1675,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") @@ -1661,7 +1703,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") @@ -1672,9 +1714,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)) utils.AssertEqual(t, nil, err, "/api/sub req") diff --git a/ctx_test.go b/ctx_test.go index 5e1ff74a8f..ee69eb4607 100644 --- a/ctx_test.go +++ b/ctx_test.go @@ -2077,7 +2077,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)) utils.AssertEqual(t, StatusOK, resp.StatusCode, "Status code") @@ -2106,7 +2106,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)) utils.AssertEqual(t, nil, err, "app.Test(req)") diff --git a/hooks_test.go b/hooks_test.go index 0b71e5e36f..932de0bd83 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_OnName(t *testing.T) { @@ -53,7 +53,7 @@ func Test_Hook_OnName(t *testing.T) { subApp.Get("/test", testSimpleHandler) subApp.Get("/test2", testSimpleHandler) - app.Mount("/sub", subApp) + app.Use("/sub", subApp) utils.AssertEqual(t, "index", buf.String()) } From ff502caa2c8237e535c2011b68b0bca1c13643b3 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 17:10:26 +0300 Subject: [PATCH 05/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20better?= =?UTF-8?q?=20variable=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 8 ++++---- group.go | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app.go b/app.go index 03f4ec5d21..76d45813ee 100644 --- a/app.go +++ b/app.go @@ -613,7 +613,7 @@ func (app *App) GetRoute(name string) Route { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...any) Router { var prefix string - var mountedApp *App + var subApp *App var handlers []Handler for i := 0; i < len(args); i++ { @@ -621,7 +621,7 @@ func (app *App) Use(args ...any) Router { case string: prefix = arg case *App: - mountedApp = arg + subApp = arg case Handler: handlers = append(handlers, arg) default: @@ -629,8 +629,8 @@ func (app *App) Use(args ...any) Router { } } - if mountedApp != nil { - app.mount(prefix, mountedApp) + if subApp != nil { + app.mount(prefix, subApp) return app } diff --git a/group.go b/group.go index 88771ead25..4d3c6c57f6 100644 --- a/group.go +++ b/group.go @@ -82,14 +82,14 @@ func (grp *Group) Name(name string) Router { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...any) Router { prefix := "" - var mountedApp *App + var subApp *App var handlers []Handler for i := 0; i < len(args); i++ { switch arg := args[i].(type) { case string: prefix = arg case *App: - mountedApp = arg + subApp = arg case Handler: handlers = append(handlers, arg) default: @@ -97,8 +97,8 @@ func (grp *Group) Use(args ...any) Router { } } - if mountedApp != nil { - grp.mount(prefix, mountedApp) + if subApp != nil { + grp.mount(prefix, subApp) return grp } From 2204bc583ad9eb06386a725ee8205fd8047e43cd Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Thu, 18 Aug 2022 17:15:04 +0300 Subject: [PATCH 06/15] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20better?= =?UTF-8?q?=20function=20naming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 2 +- app_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app.go b/app.go index 76d45813ee..b45f603cee 100644 --- a/app.go +++ b/app.go @@ -710,7 +710,7 @@ func (app *App) All(path string, handlers ...Handler) Router { } // The MountPath property contains one or more path patterns on which a sub-app was mounted. -func (app *App) Mountpath() string { +func (app *App) MountPath() string { if app.mountpath == "" { panic("mountpath cannot be used on parent app") } diff --git a/app_test.go b/app_test.go index 1cdd3948e5..28d6731a35 100644 --- a/app_test.go +++ b/app_test.go @@ -333,15 +333,15 @@ func Test_App_Mountpath(t *testing.T) { parent.Use("/sub", sub) parent.Use("/sub1", sub1) - utils.AssertEqual(t, "/sub", sub.Mountpath()) - utils.AssertEqual(t, "/sub1", sub1.Mountpath()) + utils.AssertEqual(t, "/sub", sub.MountPath()) + utils.AssertEqual(t, "/sub1", sub1.MountPath()) defer func() { if err := recover(); err != nil { utils.AssertEqual(t, "mountpath cannot be used on parent app", fmt.Sprintf("%s", err)) } }() - parent.Mountpath() + parent.MountPath() } func Test_App_OnMount(t *testing.T) { From 0d79f6a67e3162893ce1af631179a2cf38a70277 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Fri, 19 Aug 2022 10:49:05 +0300 Subject: [PATCH 07/15] =?UTF-8?q?=F0=9F=90=9B=20fix:=20not=20mounted=20che?= =?UTF-8?q?ck?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.go b/app.go index b45f603cee..a9a9409629 100644 --- a/app.go +++ b/app.go @@ -720,6 +720,10 @@ func (app *App) MountPath() string { // The mount event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. func (app *App) OnMount(callback func(parent *App)) { + if app.parent == nil { + panic("not mounted sub app to parent app") + } + if app.mountpath == "" { panic("onmount cannot be used on parent app") } From cb7b9d8945f479a7be041b9e8e18960bf86c8d3f Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Fri, 19 Aug 2022 10:50:40 +0300 Subject: [PATCH 08/15] =?UTF-8?q?=E2=9C=85=20test:=20update=20onmount=20te?= =?UTF-8?q?st=20if=20sub=20app=20is=20not=20mounted=20to=20parent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app_test.go b/app_test.go index 28d6731a35..cfae58d505 100644 --- a/app_test.go +++ b/app_test.go @@ -347,6 +347,7 @@ func Test_App_Mountpath(t *testing.T) { func Test_App_OnMount(t *testing.T) { app := New() sub := New() + sub1 := New() app.Use("/sub", sub) @@ -359,6 +360,14 @@ func Test_App_OnMount(t *testing.T) { utils.AssertEqual(t, parent != nil, true) }) + defer func() { + if err := recover(); err != nil { + utils.AssertEqual(t, "not mounted sub app to parent app", fmt.Sprintf("%s", err)) + } + }() + + sub1.OnMount(func(parent *App) {}) + defer func() { if err := recover(); err != nil { utils.AssertEqual(t, "onmount cannot be used on parent app", fmt.Sprintf("%s", err)) From 51a4cdc6935cec9db189b36bd88c01cb56f59f7b Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Sun, 21 Aug 2022 01:33:15 +0300 Subject: [PATCH 09/15] =?UTF-8?q?=F0=9F=8E=A8=20perf:=20fix=20allocs=20pro?= =?UTF-8?q?blem?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.go b/app.go index a9a9409629..b6b41ae775 100644 --- a/app.go +++ b/app.go @@ -715,7 +715,7 @@ func (app *App) MountPath() string { panic("mountpath cannot be used on parent app") } - return utils.CopyString(app.mountpath) + return app.mountpath } // The mount event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. From 3cbe99fd17ec63fcdc0934837f8c5312fb1f4ff8 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Sun, 21 Aug 2022 01:35:58 +0300 Subject: [PATCH 10/15] =?UTF-8?q?=E2=9C=85=20test:=20add=20benchmark=20for?= =?UTF-8?q?=20MountPath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app_test.go b/app_test.go index cfae58d505..13eb2bd677 100644 --- a/app_test.go +++ b/app_test.go @@ -325,7 +325,7 @@ func Test_App_Mount(t *testing.T) { utils.AssertEqual(t, uint32(2), app.handlersCount) } -func Test_App_Mountpath(t *testing.T) { +func Test_App_MountPath(t *testing.T) { parent := New() sub := New() sub1 := New() @@ -344,6 +344,23 @@ func Test_App_Mountpath(t *testing.T) { parent.MountPath() } +func Benchmark_App_MountPath(b *testing.B) { + parent := New() + sub := New() + + parent.Use("/sub", sub) + + var mp string + b.ReportAllocs() + b.ResetTimer() + + for n := 0; n < b.N; n++ { + mp = sub.MountPath() + } + + utils.AssertEqual(b, "/sub", mp) +} + func Test_App_OnMount(t *testing.T) { app := New() sub := New() From a349350939932458513296530885126173f56051 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Fri, 2 Sep 2022 14:03:06 +0300 Subject: [PATCH 11/15] =?UTF-8?q?=E2=9C=A8=20feat:=20access=20to=20app's?= =?UTF-8?q?=20mountpath?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app.go b/app.go index b6b41ae775..66cd7412e8 100644 --- a/app.go +++ b/app.go @@ -711,10 +711,6 @@ func (app *App) All(path string, handlers ...Handler) Router { // The MountPath property contains one or more path patterns on which a sub-app was mounted. func (app *App) MountPath() string { - if app.mountpath == "" { - panic("mountpath cannot be used on parent app") - } - return app.mountpath } From 400c828ab120e1ca7ccd12faf39a1f47c66ca2a6 Mon Sep 17 00:00:00 2001 From: Eren BALCI Date: Fri, 2 Sep 2022 14:04:43 +0300 Subject: [PATCH 12/15] =?UTF-8?q?=E2=9C=85=20test:=20remove=20parent=20app?= =?UTF-8?q?'s=20mountpath=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app_test.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app_test.go b/app_test.go index 13eb2bd677..3859a4490b 100644 --- a/app_test.go +++ b/app_test.go @@ -335,13 +335,6 @@ func Test_App_MountPath(t *testing.T) { utils.AssertEqual(t, "/sub", sub.MountPath()) utils.AssertEqual(t, "/sub1", sub1.MountPath()) - - defer func() { - if err := recover(); err != nil { - utils.AssertEqual(t, "mountpath cannot be used on parent app", fmt.Sprintf("%s", err)) - } - }() - parent.MountPath() } func Benchmark_App_MountPath(b *testing.B) { From 421039f41b98fc86703e3bb837264a63b59ea693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 28 Oct 2022 18:47:21 +0300 Subject: [PATCH 13/15] remove some parts --- app.go | 27 ------------------------ app_test.go | 61 ----------------------------------------------------- group.go | 2 -- 3 files changed, 90 deletions(-) diff --git a/app.go b/app.go index 66cd7412e8..9944fb28a9 100644 --- a/app.go +++ b/app.go @@ -116,10 +116,6 @@ type App struct { getString func(b []byte) string // Mounted and main apps appList map[string]*App - // If application is a parent, It returns nil. It can accessible only from sub app - parent *App - // Mounted sub app's path - mountpath string // Hooks hooks *hooks // Latest route & group @@ -469,8 +465,6 @@ func New(config ...Config) *App { getBytes: utils.UnsafeBytes, getString: utils.UnsafeString, appList: make(map[string]*App), - parent: nil, - mountpath: "", latestRoute: &Route{}, latestGroup: &Group{}, customBinders: []CustomBinder{}, @@ -709,25 +703,6 @@ func (app *App) All(path string, handlers ...Handler) Router { return app } -// The MountPath property contains one or more path patterns on which a sub-app was mounted. -func (app *App) MountPath() string { - return app.mountpath -} - -// The mount event is fired on a sub-app, when it is mounted on a parent app. The parent app is passed to the callback function. -func (app *App) OnMount(callback func(parent *App)) { - if app.parent == nil { - panic("not mounted sub app to parent app") - } - - if app.mountpath == "" { - panic("onmount cannot be used on parent app") - } - - // returns parent app in callback - callback(app.parent) -} - // Group is used for Routes with common prefix to define a new sub-router with optional middleware. // // api := app.Group("/api") @@ -1142,8 +1117,6 @@ func (app *App) mount(prefix string, sub *App) *App { // Support for configs of mounted-apps and sub-mounted-apps for mountedPrefixes, subApp := range sub.appList { app.appList[prefix+mountedPrefixes] = subApp - subApp.parent = app - subApp.mountpath = prefix + mountedPrefixes subApp.init() } diff --git a/app_test.go b/app_test.go index 3859a4490b..397b637d48 100644 --- a/app_test.go +++ b/app_test.go @@ -325,67 +325,6 @@ func Test_App_Mount(t *testing.T) { utils.AssertEqual(t, uint32(2), app.handlersCount) } -func Test_App_MountPath(t *testing.T) { - parent := New() - sub := New() - sub1 := New() - - parent.Use("/sub", sub) - parent.Use("/sub1", sub1) - - utils.AssertEqual(t, "/sub", sub.MountPath()) - utils.AssertEqual(t, "/sub1", sub1.MountPath()) -} - -func Benchmark_App_MountPath(b *testing.B) { - parent := New() - sub := New() - - parent.Use("/sub", sub) - - var mp string - b.ReportAllocs() - b.ResetTimer() - - for n := 0; n < b.N; n++ { - mp = sub.MountPath() - } - - utils.AssertEqual(b, "/sub", mp) -} - -func Test_App_OnMount(t *testing.T) { - app := New() - sub := New() - sub1 := New() - - app.Use("/sub", sub) - - sub.OnMount(func(parent *App) { - //Check parent app - utils.AssertEqual(t, app.mountpath, parent.mountpath) - }) - - sub.OnMount(func(parent *App) { - utils.AssertEqual(t, parent != nil, true) - }) - - defer func() { - if err := recover(); err != nil { - utils.AssertEqual(t, "not mounted sub app to parent app", fmt.Sprintf("%s", err)) - } - }() - - sub1.OnMount(func(parent *App) {}) - - defer func() { - if err := recover(); err != nil { - utils.AssertEqual(t, "onmount cannot be used on parent app", fmt.Sprintf("%s", err)) - } - }() - app.OnMount(func(parent *App) {}) -} - func Test_App_Use_Params(t *testing.T) { app := New() diff --git a/group.go b/group.go index 4d3c6c57f6..af5e6ffa91 100644 --- a/group.go +++ b/group.go @@ -38,8 +38,6 @@ func (grp *Group) mount(prefix string, sub *App) Router { // Support for configs of mounted-apps and sub-mounted-apps for mountedPrefixes, subApp := range sub.appList { grp.app.appList[groupPath+mountedPrefixes] = subApp - subApp.parent = grp.app - subApp.mountpath = groupPath + mountedPrefixes subApp.init() } From b4cff763fd39ce3ea024c12632198e75f6616734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 28 Oct 2022 19:02:32 +0300 Subject: [PATCH 14/15] add comment --- app.go | 25 ++++++++++++++++--------- group.go | 25 ++++++++++++++++--------- mount.go | 4 +++- mount_test.go | 26 ++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 19 deletions(-) diff --git a/app.go b/app.go index ae9a6ecabc..7faaa49cc9 100644 --- a/app.go +++ b/app.go @@ -618,16 +618,23 @@ 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 { diff --git a/group.go b/group.go index 71bad4f8ae..44cd5ddbdc 100644 --- a/group.go +++ b/group.go @@ -39,16 +39,23 @@ 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 { diff --git a/mount.go b/mount.go index 056ccbc791..ccb25eca43 100644 --- a/mount.go +++ b/mount.go @@ -58,7 +58,9 @@ 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. +// 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, "/") diff --git a/mount_test.go b/mount_test.go index a48f03768c..2db7fca75b 100644 --- a/mount_test.go +++ b/mount_test.go @@ -28,6 +28,32 @@ func Test_App_Mount(t *testing.T) { require.Equal(t, uint32(1), app.handlersCount) } +// go test -run Test_App_Mount_Multiple +func Test_App_Mount_Multiple(t *testing.T) { + micro := New() + micro.Get("/doe", func(c Ctx) error { + return c.SendStatus(StatusOK) + }) + + micro2 := New() + micro2.Get("/test", func(c Ctx) error { + return c.SendStatus(StatusOK) + }) + + app := New() + app.Use("/john", micro, micro2) + + 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") + + resp, err = app.Test(httptest.NewRequest(MethodGet, "/john/test", nil)) + require.Equal(t, nil, err, "app.Test(req)") + require.Equal(t, 200, resp.StatusCode, "Status code") + + require.Equal(t, uint32(2), app.handlersCount) +} + // go test -run Test_App_Mount_Nested func Test_App_Mount_Nested(t *testing.T) { app := New() From e83eccc45a4665441e51f7b32dc6763a01d693d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Muhammed=20Efe=20=C3=87etin?= Date: Fri, 28 Oct 2022 19:26:13 +0300 Subject: [PATCH 15/15] fix --- app.go | 10 ++++------ group.go | 10 ++++------ mount_test.go | 26 -------------------------- 3 files changed, 8 insertions(+), 38 deletions(-) diff --git a/app.go b/app.go index 7faaa49cc9..99406a1602 100644 --- a/app.go +++ b/app.go @@ -639,7 +639,7 @@ func (app *App) GetRoutes(filterUseOption ...bool) []Route { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (app *App) Use(args ...any) Router { var prefix string - var subApps []*App + var subApp *App var handlers []Handler for i := 0; i < len(args); i++ { @@ -647,7 +647,7 @@ func (app *App) Use(args ...any) Router { case string: prefix = arg case *App: - subApps = append(subApps, arg) + subApp = arg case Handler: handlers = append(handlers, arg) default: @@ -655,10 +655,8 @@ func (app *App) Use(args ...any) Router { } } - if len(subApps) > 0 { - for _, subApp := range subApps { - app.mount(prefix, subApp) - } + if subApp != nil { + app.mount(prefix, subApp) return app } diff --git a/group.go b/group.go index 44cd5ddbdc..3470f06027 100644 --- a/group.go +++ b/group.go @@ -60,7 +60,7 @@ func (grp *Group) Name(name string) Router { // This method will match all HTTP verbs: GET, POST, PUT, HEAD etc... func (grp *Group) Use(args ...any) Router { prefix := "" - var subApps []*App + var subApp *App var handlers []Handler for i := 0; i < len(args); i++ { @@ -68,7 +68,7 @@ func (grp *Group) Use(args ...any) Router { case string: prefix = arg case *App: - subApps = append(subApps, arg) + subApp = arg case Handler: handlers = append(handlers, arg) default: @@ -76,10 +76,8 @@ func (grp *Group) Use(args ...any) Router { } } - if len(subApps) > 0 { - for _, subApp := range subApps { - grp.mount(prefix, subApp) - } + if subApp != nil { + grp.mount(prefix, subApp) return grp } diff --git a/mount_test.go b/mount_test.go index 2db7fca75b..a48f03768c 100644 --- a/mount_test.go +++ b/mount_test.go @@ -28,32 +28,6 @@ func Test_App_Mount(t *testing.T) { require.Equal(t, uint32(1), app.handlersCount) } -// go test -run Test_App_Mount_Multiple -func Test_App_Mount_Multiple(t *testing.T) { - micro := New() - micro.Get("/doe", func(c Ctx) error { - return c.SendStatus(StatusOK) - }) - - micro2 := New() - micro2.Get("/test", func(c Ctx) error { - return c.SendStatus(StatusOK) - }) - - app := New() - app.Use("/john", micro, micro2) - - 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") - - resp, err = app.Test(httptest.NewRequest(MethodGet, "/john/test", nil)) - require.Equal(t, nil, err, "app.Test(req)") - require.Equal(t, 200, resp.StatusCode, "Status code") - - require.Equal(t, uint32(2), app.handlersCount) -} - // go test -run Test_App_Mount_Nested func Test_App_Mount_Nested(t *testing.T) { app := New()