added error event hooks
This commit is contained in:
parent
23fbfab63a
commit
02f72638b8
|
@ -6,6 +6,8 @@
|
||||||
```go
|
```go
|
||||||
app.OnBeforeBootstrap()
|
app.OnBeforeBootstrap()
|
||||||
app.OnAfterBootstrap()
|
app.OnAfterBootstrap()
|
||||||
|
app.OnBeforeApiError()
|
||||||
|
app.OnAfterApiError()
|
||||||
app.OnRealtimeDisconnectRequest()
|
app.OnRealtimeDisconnectRequest()
|
||||||
app.OnRealtimeBeforeMessageSend()
|
app.OnRealtimeBeforeMessageSend()
|
||||||
app.OnRealtimeAfterMessageSend()
|
app.OnRealtimeAfterMessageSend()
|
||||||
|
|
26
apis/base.go
26
apis/base.go
|
@ -64,19 +64,27 @@ func InitApi(app core.App) (*echo.Echo, error) {
|
||||||
apiErr = NewBadRequestError("", err)
|
apiErr = NewBadRequestError("", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send response
|
event := &core.ApiErrorEvent{
|
||||||
var cErr error
|
HttpContext: c,
|
||||||
if c.Request().Method == http.MethodHead {
|
Error: apiErr,
|
||||||
// @see https://github.com/labstack/echo/issues/608
|
|
||||||
cErr = c.NoContent(apiErr.Code)
|
|
||||||
} else {
|
|
||||||
cErr = c.JSON(apiErr.Code, apiErr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hookErr := app.OnBeforeApiError().Trigger(event, func(e *core.ApiErrorEvent) error {
|
||||||
|
// Send response
|
||||||
|
if e.HttpContext.Request().Method == http.MethodHead {
|
||||||
|
// @see https://github.com/labstack/echo/issues/608
|
||||||
|
return e.HttpContext.NoContent(apiErr.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.HttpContext.JSON(apiErr.Code, apiErr)
|
||||||
|
})
|
||||||
|
|
||||||
// truly rare case; eg. client already disconnected
|
// truly rare case; eg. client already disconnected
|
||||||
if cErr != nil && app.IsDebug() {
|
if hookErr != nil && app.IsDebug() {
|
||||||
log.Println(cErr)
|
log.Println(hookErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
app.OnAfterApiError().Trigger(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// admin ui routes
|
// admin ui routes
|
||||||
|
|
10
core/app.go
10
core/app.go
|
@ -92,6 +92,16 @@ type App interface {
|
||||||
// allowing you to adjust its options and attach new routes.
|
// allowing you to adjust its options and attach new routes.
|
||||||
OnBeforeServe() *hook.Hook[*ServeEvent]
|
OnBeforeServe() *hook.Hook[*ServeEvent]
|
||||||
|
|
||||||
|
// OnBeforeApiError hook is triggered right before sending an error API
|
||||||
|
// response to the client, allowing you to further modify the error data
|
||||||
|
// or to return a completely different API response (using [hook.StopPropagation]).
|
||||||
|
OnBeforeApiError() *hook.Hook[*ApiErrorEvent]
|
||||||
|
|
||||||
|
// OnAfterApiError hook is triggered right after sending an error API
|
||||||
|
// response to the client.
|
||||||
|
// It could be used to log the final API error in external services.
|
||||||
|
OnAfterApiError() *hook.Hook[*ApiErrorEvent]
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
// Dao event hooks
|
// Dao event hooks
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
12
core/base.go
12
core/base.go
|
@ -43,6 +43,8 @@ type BaseApp struct {
|
||||||
onBeforeBootstrap *hook.Hook[*BootstrapEvent]
|
onBeforeBootstrap *hook.Hook[*BootstrapEvent]
|
||||||
onAfterBootstrap *hook.Hook[*BootstrapEvent]
|
onAfterBootstrap *hook.Hook[*BootstrapEvent]
|
||||||
onBeforeServe *hook.Hook[*ServeEvent]
|
onBeforeServe *hook.Hook[*ServeEvent]
|
||||||
|
onBeforeApiError *hook.Hook[*ApiErrorEvent]
|
||||||
|
onAfterApiError *hook.Hook[*ApiErrorEvent]
|
||||||
|
|
||||||
// dao event hooks
|
// dao event hooks
|
||||||
onModelBeforeCreate *hook.Hook[*ModelEvent]
|
onModelBeforeCreate *hook.Hook[*ModelEvent]
|
||||||
|
@ -135,6 +137,8 @@ func NewBaseApp(dataDir string, encryptionEnv string, isDebug bool) *BaseApp {
|
||||||
onBeforeBootstrap: &hook.Hook[*BootstrapEvent]{},
|
onBeforeBootstrap: &hook.Hook[*BootstrapEvent]{},
|
||||||
onAfterBootstrap: &hook.Hook[*BootstrapEvent]{},
|
onAfterBootstrap: &hook.Hook[*BootstrapEvent]{},
|
||||||
onBeforeServe: &hook.Hook[*ServeEvent]{},
|
onBeforeServe: &hook.Hook[*ServeEvent]{},
|
||||||
|
onBeforeApiError: &hook.Hook[*ApiErrorEvent]{},
|
||||||
|
onAfterApiError: &hook.Hook[*ApiErrorEvent]{},
|
||||||
|
|
||||||
// dao event hooks
|
// dao event hooks
|
||||||
onModelBeforeCreate: &hook.Hook[*ModelEvent]{},
|
onModelBeforeCreate: &hook.Hook[*ModelEvent]{},
|
||||||
|
@ -405,6 +409,14 @@ func (app *BaseApp) OnBeforeServe() *hook.Hook[*ServeEvent] {
|
||||||
return app.onBeforeServe
|
return app.onBeforeServe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (app *BaseApp) OnBeforeApiError() *hook.Hook[*ApiErrorEvent] {
|
||||||
|
return app.onBeforeApiError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (app *BaseApp) OnAfterApiError() *hook.Hook[*ApiErrorEvent] {
|
||||||
|
return app.onAfterApiError
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// Dao event hooks
|
// Dao event hooks
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
|
@ -25,6 +25,11 @@ type ServeEvent struct {
|
||||||
Router *echo.Echo
|
Router *echo.Echo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ApiErrorEvent struct {
|
||||||
|
HttpContext echo.Context
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// Model DAO events data
|
// Model DAO events data
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
14
tests/api.go
14
tests/api.go
|
@ -134,6 +134,20 @@ func (scenario *ApiScenario) Test(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to minimize the breaking changes we always expect the error
|
||||||
|
// events to be called on API error
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
|
if scenario.ExpectedEvents == nil {
|
||||||
|
scenario.ExpectedEvents = map[string]int{}
|
||||||
|
}
|
||||||
|
if _, ok := scenario.ExpectedEvents["OnBeforeApiError"]; !ok {
|
||||||
|
scenario.ExpectedEvents["OnBeforeApiError"] = 1
|
||||||
|
}
|
||||||
|
if _, ok := scenario.ExpectedEvents["OnAfterApiError"]; !ok {
|
||||||
|
scenario.ExpectedEvents["OnAfterApiError"] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(testApp.EventCalls) > len(scenario.ExpectedEvents) {
|
if len(testApp.EventCalls) > len(scenario.ExpectedEvents) {
|
||||||
t.Errorf("[%s] Expected events %v, got %v", prefix, scenario.ExpectedEvents, testApp.EventCalls)
|
t.Errorf("[%s] Expected events %v, got %v", prefix, scenario.ExpectedEvents, testApp.EventCalls)
|
||||||
}
|
}
|
||||||
|
|
10
tests/app.go
10
tests/app.go
|
@ -87,6 +87,16 @@ func NewTestApp(optTestDataDir ...string) (*TestApp, error) {
|
||||||
TestMailer: &TestMailer{},
|
TestMailer: &TestMailer{},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.OnBeforeApiError().Add(func(e *core.ApiErrorEvent) error {
|
||||||
|
t.EventCalls["OnBeforeApiError"]++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
t.OnAfterApiError().Add(func(e *core.ApiErrorEvent) error {
|
||||||
|
t.EventCalls["OnAfterApiError"]++
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
t.OnModelBeforeCreate().Add(func(e *core.ModelEvent) error {
|
t.OnModelBeforeCreate().Add(func(e *core.ModelEvent) error {
|
||||||
t.EventCalls["OnModelBeforeCreate"]++
|
t.EventCalls["OnModelBeforeCreate"]++
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue