You've already forked pocketbase
							
							
				mirror of
				https://github.com/pocketbase/pocketbase.git
				synced 2025-10-31 08:37:38 +02:00 
			
		
		
		
	added support for optional Model and Record event hook tags
This commit is contained in:
		| @@ -56,6 +56,8 @@ | ||||
|   store.GetAll() map[string]T | ||||
|   ``` | ||||
|  | ||||
| - Added tagged/proxy hook for all Record and Model events (@todo document). | ||||
|  | ||||
|  | ||||
| ## v0.11.4 | ||||
|  | ||||
|   | ||||
| @@ -39,11 +39,10 @@ func (api *adminApi) authResponse(c echo.Context, admin *models.Admin) error { | ||||
| 		return NewBadRequestError("Failed to create auth token.", tokenErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminAuthEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 		Token:       token, | ||||
| 	} | ||||
| 	event := new(core.AdminAuthEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
| 	event.Token = token | ||||
|  | ||||
| 	return api.app.OnAdminAuthRequest().Trigger(event, func(e *core.AdminAuthEvent) error { | ||||
| 		return e.HttpContext.JSON(200, map[string]any{ | ||||
| @@ -59,10 +58,9 @@ func (api *adminApi) authRefresh(c echo.Context) error { | ||||
| 		return NewNotFoundError("Missing auth admin context.", nil) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminAuthRefreshEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
| 	event := new(core.AdminAuthRefreshEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
|  | ||||
| 	handlerErr := api.app.OnAdminBeforeAuthRefreshRequest().Trigger(event, func(e *core.AdminAuthRefreshEvent) error { | ||||
| 		return api.authResponse(e.HttpContext, e.Admin) | ||||
| @@ -83,11 +81,10 @@ func (api *adminApi) authWithPassword(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminAuthWithPasswordEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Password:    form.Password, | ||||
| 		Identity:    form.Identity, | ||||
| 	} | ||||
| 	event := new(core.AdminAuthWithPasswordEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Password = form.Password | ||||
| 	event.Identity = form.Identity | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] { | ||||
| 		return func(admin *models.Admin) error { | ||||
| @@ -122,9 +119,8 @@ func (api *adminApi) requestPasswordReset(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while validating the form.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminRequestPasswordResetEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.AdminRequestPasswordResetEvent) | ||||
| 	event.HttpContext = c | ||||
|  | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] { | ||||
| 		return func(Admin *models.Admin) error { | ||||
| @@ -165,9 +161,8 @@ func (api *adminApi) confirmPasswordReset(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", readErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminConfirmPasswordResetEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.AdminConfirmPasswordResetEvent) | ||||
| 	event.HttpContext = c | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] { | ||||
| 		return func(admin *models.Admin) error { | ||||
| @@ -207,11 +202,10 @@ func (api *adminApi) list(c echo.Context) error { | ||||
| 		return NewBadRequestError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminsListEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admins:      admins, | ||||
| 		Result:      result, | ||||
| 	} | ||||
| 	event := new(core.AdminsListEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admins = admins | ||||
| 	event.Result = result | ||||
|  | ||||
| 	return api.app.OnAdminsListRequest().Trigger(event, func(e *core.AdminsListEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Result) | ||||
| @@ -229,10 +223,9 @@ func (api *adminApi) view(c echo.Context) error { | ||||
| 		return NewNotFoundError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminViewEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
| 	event := new(core.AdminViewEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
|  | ||||
| 	return api.app.OnAdminViewRequest().Trigger(event, func(e *core.AdminViewEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Admin) | ||||
| @@ -249,10 +242,9 @@ func (api *adminApi) create(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminCreateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
| 	event := new(core.AdminCreateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
|  | ||||
| 	// create the admin | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] { | ||||
| @@ -296,10 +288,9 @@ func (api *adminApi) update(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminUpdateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
| 	event := new(core.AdminUpdateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
|  | ||||
| 	// update the admin | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Admin]) forms.InterceptorNextFunc[*models.Admin] { | ||||
| @@ -336,10 +327,9 @@ func (api *adminApi) delete(c echo.Context) error { | ||||
| 		return NewNotFoundError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminDeleteEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
| 	event := new(core.AdminDeleteEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Admin = admin | ||||
|  | ||||
| 	handlerErr := api.app.OnAdminBeforeDeleteRequest().Trigger(event, func(e *core.AdminDeleteEvent) error { | ||||
| 		if err := api.app.Dao().DeleteAdmin(e.Admin); err != nil { | ||||
|   | ||||
| @@ -71,10 +71,9 @@ func InitApi(app core.App) (*echo.Echo, error) { | ||||
| 			apiErr = NewBadRequestError("", err) | ||||
| 		} | ||||
|  | ||||
| 		event := &core.ApiErrorEvent{ | ||||
| 			HttpContext: c, | ||||
| 			Error:       apiErr, | ||||
| 		} | ||||
| 		event := new(core.ApiErrorEvent) | ||||
| 		event.HttpContext = c | ||||
| 		event.Error = apiErr | ||||
|  | ||||
| 		// send error response | ||||
| 		hookErr := app.OnBeforeApiError().Trigger(event, func(e *core.ApiErrorEvent) error { | ||||
|   | ||||
| @@ -43,11 +43,10 @@ func (api *collectionApi) list(c echo.Context) error { | ||||
| 		return NewBadRequestError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionsListEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collections: collections, | ||||
| 		Result:      result, | ||||
| 	} | ||||
| 	event := new(core.CollectionsListEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collections = collections | ||||
| 	event.Result = result | ||||
|  | ||||
| 	return api.app.OnCollectionsListRequest().Trigger(event, func(e *core.CollectionsListEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Result) | ||||
| @@ -60,10 +59,9 @@ func (api *collectionApi) view(c echo.Context) error { | ||||
| 		return NewNotFoundError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionViewEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
| 	event := new(core.CollectionViewEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	return api.app.OnCollectionViewRequest().Trigger(event, func(e *core.CollectionViewEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Collection) | ||||
| @@ -80,10 +78,9 @@ func (api *collectionApi) create(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionCreateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
| 	event := new(core.CollectionCreateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	// create the collection | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Collection]) forms.InterceptorNextFunc[*models.Collection] { | ||||
| @@ -122,10 +119,9 @@ func (api *collectionApi) update(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionUpdateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
| 	event := new(core.CollectionUpdateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	// update the collection | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Collection]) forms.InterceptorNextFunc[*models.Collection] { | ||||
| @@ -157,10 +153,9 @@ func (api *collectionApi) delete(c echo.Context) error { | ||||
| 		return NewNotFoundError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionDeleteEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
| 	event := new(core.CollectionDeleteEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	handlerErr := api.app.OnCollectionBeforeDeleteRequest().Trigger(event, func(e *core.CollectionDeleteEvent) error { | ||||
| 		if err := api.app.Dao().DeleteCollection(e.Collection); err != nil { | ||||
| @@ -187,10 +182,9 @@ func (api *collectionApi) bulkImport(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionsImportEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collections: form.Collections, | ||||
| 	} | ||||
| 	event := new(core.CollectionsImportEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collections = form.Collections | ||||
|  | ||||
| 	// import collections | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[[]*models.Collection]) forms.InterceptorNextFunc[[]*models.Collection] { | ||||
|   | ||||
							
								
								
									
										15
									
								
								apis/file.go
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								apis/file.go
									
									
									
									
									
								
							| @@ -84,14 +84,13 @@ func (api *fileApi) download(c echo.Context) error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	event := &core.FileDownloadEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 		Collection:  collection, | ||||
| 		FileField:   fileField, | ||||
| 		ServedPath:  servedPath, | ||||
| 		ServedName:  servedName, | ||||
| 	} | ||||
| 	event := new(core.FileDownloadEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
| 	event.FileField = fileField | ||||
| 	event.ServedPath = servedPath | ||||
| 	event.ServedName = servedName | ||||
|  | ||||
| 	return api.app.OnFileDownloadRequest().Trigger(event, func(e *core.FileDownloadEvent) error { | ||||
| 		res := e.HttpContext.Response() | ||||
|   | ||||
| @@ -55,10 +55,10 @@ func (api *recordAuthApi) authRefresh(c echo.Context) error { | ||||
| 		return NewNotFoundError("Missing auth record context.", nil) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordAuthRefreshEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordAuthRefreshEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = record.Collection() | ||||
| 	event.Record = record | ||||
|  | ||||
| 	handlerErr := api.app.OnRecordBeforeAuthRefreshRequest().Trigger(event, func(e *core.RecordAuthRefreshEvent) error { | ||||
| 		return RecordAuthResponse(api.app, e.HttpContext, e.Record, nil) | ||||
| @@ -204,9 +204,9 @@ func (api *recordAuthApi) authWithOAuth2(c echo.Context) error { | ||||
| 		}) | ||||
| 	}) | ||||
|  | ||||
| 	event := &core.RecordAuthWithOAuth2Event{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordAuthWithOAuth2Event) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	_, _, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*forms.RecordOAuth2LoginData]) forms.InterceptorNextFunc[*forms.RecordOAuth2LoginData] { | ||||
| 		return func(data *forms.RecordOAuth2LoginData) error { | ||||
| @@ -249,11 +249,11 @@ func (api *recordAuthApi) authWithPassword(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", readErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordAuthWithPasswordEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Password:    form.Password, | ||||
| 		Identity:    form.Identity, | ||||
| 	} | ||||
| 	event := new(core.RecordAuthWithPasswordEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Password = form.Password | ||||
| 	event.Identity = form.Identity | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -298,9 +298,9 @@ func (api *recordAuthApi) requestPasswordReset(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while validating the form.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordRequestPasswordResetEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordRequestPasswordResetEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -346,9 +346,9 @@ func (api *recordAuthApi) confirmPasswordReset(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", readErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordConfirmPasswordResetEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordConfirmPasswordResetEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -388,9 +388,9 @@ func (api *recordAuthApi) requestVerification(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while validating the form.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordRequestVerificationEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordRequestVerificationEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -436,9 +436,9 @@ func (api *recordAuthApi) confirmVerification(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", readErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordConfirmVerificationEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordConfirmVerificationEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -464,6 +464,11 @@ func (api *recordAuthApi) confirmVerification(c echo.Context) error { | ||||
| } | ||||
|  | ||||
| func (api *recordAuthApi) requestEmailChange(c echo.Context) error { | ||||
| 	collection, _ := c.Get(ContextCollectionKey).(*models.Collection) | ||||
| 	if collection == nil { | ||||
| 		return NewNotFoundError("Missing collection context.", nil) | ||||
| 	} | ||||
|  | ||||
| 	record, _ := c.Get(ContextAuthRecordKey).(*models.Record) | ||||
| 	if record == nil { | ||||
| 		return NewUnauthorizedError("The request requires valid auth record.", nil) | ||||
| @@ -474,10 +479,10 @@ func (api *recordAuthApi) requestEmailChange(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordRequestEmailChangeEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordRequestEmailChangeEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
|  | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -509,9 +514,9 @@ func (api *recordAuthApi) confirmEmailChange(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", readErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordConfirmEmailChangeEvent{ | ||||
| 		HttpContext: c, | ||||
| 	} | ||||
| 	event := new(core.RecordConfirmEmailChangeEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
|  | ||||
| 	_, submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| 		return func(record *models.Record) error { | ||||
| @@ -557,11 +562,11 @@ func (api *recordAuthApi) listExternalAuths(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to fetch the external auths for the specified auth record.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordListExternalAuthsEvent{ | ||||
| 		HttpContext:   c, | ||||
| 		Record:        record, | ||||
| 		ExternalAuths: externalAuths, | ||||
| 	} | ||||
| 	event := new(core.RecordListExternalAuthsEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
| 	event.ExternalAuths = externalAuths | ||||
|  | ||||
| 	return api.app.OnRecordListExternalAuthsRequest().Trigger(event, func(e *core.RecordListExternalAuthsEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.ExternalAuths) | ||||
| @@ -590,11 +595,11 @@ func (api *recordAuthApi) unlinkExternalAuth(c echo.Context) error { | ||||
| 		return NewNotFoundError("Missing external auth provider relation.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordUnlinkExternalAuthEvent{ | ||||
| 		HttpContext:  c, | ||||
| 		Record:       record, | ||||
| 		ExternalAuth: externalAuth, | ||||
| 	} | ||||
| 	event := new(core.RecordUnlinkExternalAuthEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
| 	event.ExternalAuth = externalAuth | ||||
|  | ||||
| 	handlerErr := api.app.OnRecordBeforeUnlinkExternalAuthRequest().Trigger(event, func(e *core.RecordUnlinkExternalAuthEvent) error { | ||||
| 		if err := api.app.Dao().DeleteExternalAuth(externalAuth); err != nil { | ||||
|   | ||||
| @@ -83,12 +83,11 @@ func (api *recordApi) list(c echo.Context) error { | ||||
|  | ||||
| 	result.Items = records | ||||
|  | ||||
| 	event := &core.RecordsListEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Collection:  collection, | ||||
| 		Records:     records, | ||||
| 		Result:      result, | ||||
| 	} | ||||
| 	event := new(core.RecordsListEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Records = records | ||||
| 	event.Result = result | ||||
|  | ||||
| 	return api.app.OnRecordsListRequest().Trigger(event, func(e *core.RecordsListEvent) error { | ||||
| 		if err := EnrichRecords(e.HttpContext, api.app.Dao(), e.Records); err != nil && api.app.IsDebug() { | ||||
| @@ -135,10 +134,10 @@ func (api *recordApi) view(c echo.Context) error { | ||||
| 		return NewNotFoundError("", fetchErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordViewEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordViewEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
|  | ||||
| 	return api.app.OnRecordViewRequest().Trigger(event, func(e *core.RecordViewEvent) error { | ||||
| 		if err := EnrichRecord(e.HttpContext, api.app.Dao(), e.Record); err != nil && api.app.IsDebug() { | ||||
| @@ -218,10 +217,10 @@ func (api *recordApi) create(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordCreateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordCreateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
|  | ||||
| 	// create the record | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| @@ -306,10 +305,10 @@ func (api *recordApi) update(c echo.Context) error { | ||||
| 		return NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordUpdateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordUpdateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
|  | ||||
| 	// update the record | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*models.Record]) forms.InterceptorNextFunc[*models.Record] { | ||||
| @@ -375,10 +374,10 @@ func (api *recordApi) delete(c echo.Context) error { | ||||
| 		return NewNotFoundError("", fetchErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordDeleteEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      record, | ||||
| 	} | ||||
| 	event := new(core.RecordDeleteEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = collection | ||||
| 	event.Record = record | ||||
|  | ||||
| 	handlerErr := api.app.OnRecordBeforeDeleteRequest().Trigger(event, func(e *core.RecordDeleteEvent) error { | ||||
| 		// delete the record | ||||
|   | ||||
| @@ -51,12 +51,12 @@ func RecordAuthResponse(app core.App, c echo.Context, authRecord *models.Record, | ||||
| 		return NewBadRequestError("Failed to create auth token.", tokenErr) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordAuthEvent{ | ||||
| 		HttpContext: c, | ||||
| 		Record:      authRecord, | ||||
| 		Token:       token, | ||||
| 		Meta:        meta, | ||||
| 	} | ||||
| 	event := new(core.RecordAuthEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.Collection = authRecord.Collection() | ||||
| 	event.Record = authRecord | ||||
| 	event.Token = token | ||||
| 	event.Meta = meta | ||||
|  | ||||
| 	return app.OnRecordAuthRequest().Trigger(event, func(e *core.RecordAuthEvent) error { | ||||
| 		// allow always returning the email address of the authenticated account | ||||
| @@ -93,17 +93,17 @@ func RecordAuthResponse(app core.App, c echo.Context, authRecord *models.Record, | ||||
| } | ||||
|  | ||||
| // EnrichRecord parses the request context and enrich the provided record: | ||||
| // - expands relations (if defaultExpands and/or ?expand query param is set) | ||||
| // - ensures that the emails of the auth record and its expanded auth relations | ||||
| //   are visibe only for the current logged admin, record owner or record with manage access | ||||
| //   - expands relations (if defaultExpands and/or ?expand query param is set) | ||||
| //   - ensures that the emails of the auth record and its expanded auth relations | ||||
| //     are visibe only for the current logged admin, record owner or record with manage access | ||||
| func EnrichRecord(c echo.Context, dao *daos.Dao, record *models.Record, defaultExpands ...string) error { | ||||
| 	return EnrichRecords(c, dao, []*models.Record{record}, defaultExpands...) | ||||
| } | ||||
|  | ||||
| // EnrichRecords parses the request context and enriches the provided records: | ||||
| // - expands relations (if defaultExpands and/or ?expand query param is set) | ||||
| // - ensures that the emails of the auth records and their expanded auth relations | ||||
| //   are visibe only for the current logged admin, record owner or record with manage access | ||||
| //   - expands relations (if defaultExpands and/or ?expand query param is set) | ||||
| //   - ensures that the emails of the auth records and their expanded auth relations | ||||
| //     are visibe only for the current logged admin, record owner or record with manage access | ||||
| func EnrichRecords(c echo.Context, dao *daos.Dao, records []*models.Record, defaultExpands ...string) error { | ||||
| 	requestData := RequestData(c) | ||||
|  | ||||
|   | ||||
| @@ -34,10 +34,9 @@ func (api *settingsApi) list(c echo.Context) error { | ||||
| 		return NewBadRequestError("", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.SettingsListEvent{ | ||||
| 		HttpContext:      c, | ||||
| 		RedactedSettings: settings, | ||||
| 	} | ||||
| 	event := new(core.SettingsListEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.RedactedSettings = settings | ||||
|  | ||||
| 	return api.app.OnSettingsListRequest().Trigger(event, func(e *core.SettingsListEvent) error { | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.RedactedSettings) | ||||
| @@ -52,10 +51,9 @@ func (api *settingsApi) set(c echo.Context) error { | ||||
| 		return NewBadRequestError("An error occurred while loading the submitted data.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.SettingsUpdateEvent{ | ||||
| 		HttpContext: c, | ||||
| 		OldSettings: api.app.Settings(), | ||||
| 	} | ||||
| 	event := new(core.SettingsUpdateEvent) | ||||
| 	event.HttpContext = c | ||||
| 	event.OldSettings = api.app.Settings() | ||||
|  | ||||
| 	// update the settings | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc[*settings.Settings]) forms.InterceptorNextFunc[*settings.Settings] { | ||||
|   | ||||
							
								
								
									
										218
									
								
								core/app.go
									
									
									
									
									
								
							
							
						
						
									
										218
									
								
								core/app.go
									
									
									
									
									
								
							| @@ -124,27 +124,51 @@ type App interface { | ||||
|  | ||||
| 	// OnModelBeforeCreate hook is triggered before inserting a new | ||||
| 	// entry in the DB, allowing you to modify or validate the stored data. | ||||
| 	OnModelBeforeCreate() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelBeforeCreate(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// OnModelAfterCreate hook is triggered after successfully | ||||
| 	// inserting a new entry in the DB. | ||||
| 	OnModelAfterCreate() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelAfterCreate(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// OnModelBeforeUpdate hook is triggered before updating existing | ||||
| 	// entry in the DB, allowing you to modify or validate the stored data. | ||||
| 	OnModelBeforeUpdate() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelBeforeUpdate(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// OnModelAfterUpdate hook is triggered after successfully updating | ||||
| 	// existing entry in the DB. | ||||
| 	OnModelAfterUpdate() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelAfterUpdate(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// OnModelBeforeDelete hook is triggered before deleting an | ||||
| 	// existing entry from the DB. | ||||
| 	OnModelBeforeDelete() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelBeforeDelete(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// OnModelAfterDelete is triggered after successfully deleting an | ||||
| 	// existing entry from the DB. | ||||
| 	OnModelAfterDelete() *hook.Hook[*ModelEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" | ||||
| 	// (table names and/or the Collection id for Record models) | ||||
| 	// to filter any the newly attached event data handler. | ||||
| 	OnModelAfterDelete(tags ...string) *hook.TaggedHook[*ModelEvent] | ||||
|  | ||||
| 	// --------------------------------------------------------------- | ||||
| 	// Mailer event hooks | ||||
| @@ -166,33 +190,51 @@ type App interface { | ||||
| 	// | ||||
| 	// Could be used to send your own custom email template if | ||||
| 	// [hook.StopPropagation] is returned in one of its listeners. | ||||
| 	OnMailerBeforeRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerBeforeRecordResetPasswordSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// OnMailerAfterRecordResetPasswordSend hook is triggered after | ||||
| 	// an auth record password reset email was successfully sent. | ||||
| 	OnMailerAfterRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerAfterRecordResetPasswordSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// OnMailerBeforeRecordVerificationSend hook is triggered right before | ||||
| 	// sending a verification email to an auth record. | ||||
| 	// | ||||
| 	// Could be used to send your own custom email template if | ||||
| 	// [hook.StopPropagation] is returned in one of its listeners. | ||||
| 	OnMailerBeforeRecordVerificationSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerBeforeRecordVerificationSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// OnMailerAfterRecordVerificationSend hook is triggered after a | ||||
| 	// verification email was successfully sent to an auth record. | ||||
| 	OnMailerAfterRecordVerificationSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerAfterRecordVerificationSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// OnMailerBeforeRecordChangeEmailSend hook is triggered right before | ||||
| 	// sending a confirmation new address email to an auth record. | ||||
| 	// | ||||
| 	// Could be used to send your own custom email template if | ||||
| 	// [hook.StopPropagation] is returned in one of its listeners. | ||||
| 	OnMailerBeforeRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerBeforeRecordChangeEmailSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// OnMailerAfterRecordChangeEmailSend hook is triggered after a | ||||
| 	// verification email was successfully sent to an auth record. | ||||
| 	OnMailerAfterRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnMailerAfterRecordChangeEmailSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] | ||||
|  | ||||
| 	// --------------------------------------------------------------- | ||||
| 	// Realtime API event hooks | ||||
| @@ -257,7 +299,7 @@ type App interface { | ||||
| 	// | ||||
| 	// Could be used to validate or modify the file response before | ||||
| 	// returning it to the client. | ||||
| 	OnFileDownloadRequest() *hook.Hook[*FileDownloadEvent] | ||||
| 	OnFileDownloadRequest(tags ...string) *hook.TaggedHook[*FileDownloadEvent] | ||||
|  | ||||
| 	// --------------------------------------------------------------- | ||||
| 	// Admin API event hooks | ||||
| @@ -366,18 +408,27 @@ type App interface { | ||||
| 	// | ||||
| 	// Could be used to additionally validate or modify the authenticated | ||||
| 	// record data and token. | ||||
| 	OnRecordAuthRequest() *hook.Hook[*RecordAuthEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAuthRequest(tags ...string) *hook.TaggedHook[*RecordAuthEvent] | ||||
|  | ||||
| 	// OnRecordBeforeAuthWithPasswordRequest hook is triggered before each Record | ||||
| 	// auth with password API request (after request data load and before password validation). | ||||
| 	// | ||||
| 	// Could be used to implement for example a custom password validation | ||||
| 	// or to locate a different Record identity (by assigning [RecordAuthWithPasswordEvent.Record]). | ||||
| 	OnRecordBeforeAuthWithPasswordRequest() *hook.Hook[*RecordAuthWithPasswordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeAuthWithPasswordRequest(tags ...string) *hook.TaggedHook[*RecordAuthWithPasswordEvent] | ||||
|  | ||||
| 	// OnRecordAfterAuthWithPasswordRequest hook is triggered after each | ||||
| 	// successful Record auth with password API request. | ||||
| 	OnRecordAfterAuthWithPasswordRequest() *hook.Hook[*RecordAuthWithPasswordEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterAuthWithPasswordRequest(tags ...string) *hook.TaggedHook[*RecordAuthWithPasswordEvent] | ||||
|  | ||||
| 	// OnRecordBeforeAuthWithOAuth2Request hook is triggered before each Record | ||||
| 	// OAuth2 sign-in/sign-up API request (after token exchange and before external provider linking). | ||||
| @@ -387,104 +438,161 @@ type App interface { | ||||
| 	// | ||||
| 	// To assign or link a different existing record model you can | ||||
| 	// overwrite/modify the [RecordAuthWithOAuth2Event.Record] field. | ||||
| 	OnRecordBeforeAuthWithOAuth2Request() *hook.Hook[*RecordAuthWithOAuth2Event] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeAuthWithOAuth2Request(tags ...string) *hook.TaggedHook[*RecordAuthWithOAuth2Event] | ||||
|  | ||||
| 	// OnRecordAfterAuthWithOAuth2Request hook is triggered after each | ||||
| 	// successful Record OAuth2 API request. | ||||
| 	OnRecordAfterAuthWithOAuth2Request() *hook.Hook[*RecordAuthWithOAuth2Event] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterAuthWithOAuth2Request(tags ...string) *hook.TaggedHook[*RecordAuthWithOAuth2Event] | ||||
|  | ||||
| 	// OnRecordBeforeAuthRefreshRequest hook is triggered before each Record | ||||
| 	// auth refresh API request (right before generating a new auth token). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different auth refresh behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeAuthRefreshRequest() *hook.Hook[*RecordAuthRefreshEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeAuthRefreshRequest(tags ...string) *hook.TaggedHook[*RecordAuthRefreshEvent] | ||||
|  | ||||
| 	// OnRecordAfterAuthRefreshRequest hook is triggered after each | ||||
| 	// successful auth refresh API request (right after generating a new auth token). | ||||
| 	OnRecordAfterAuthRefreshRequest() *hook.Hook[*RecordAuthRefreshEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterAuthRefreshRequest(tags ...string) *hook.TaggedHook[*RecordAuthRefreshEvent] | ||||
|  | ||||
| 	// OnRecordBeforeRequestPasswordResetRequest hook is triggered before each Record | ||||
| 	// request password reset API request (after request data load and before sending the reset email). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different password reset behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeRequestPasswordResetRequest() *hook.Hook[*RecordRequestPasswordResetEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeRequestPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordRequestPasswordResetEvent] | ||||
|  | ||||
| 	// OnRecordAfterRequestPasswordResetRequest hook is triggered after each | ||||
| 	// successful request password reset API request. | ||||
| 	OnRecordAfterRequestPasswordResetRequest() *hook.Hook[*RecordRequestPasswordResetEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterRequestPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordRequestPasswordResetEvent] | ||||
|  | ||||
| 	// OnRecordBeforeConfirmPasswordResetRequest hook is triggered before each Record | ||||
| 	// confirm password reset API request (after request data load and before persistence). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different persistence behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeConfirmPasswordResetRequest() *hook.Hook[*RecordConfirmPasswordResetEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeConfirmPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordConfirmPasswordResetEvent] | ||||
|  | ||||
| 	// OnRecordAfterConfirmPasswordResetRequest hook is triggered after each | ||||
| 	// successful confirm password reset API request. | ||||
| 	OnRecordAfterConfirmPasswordResetRequest() *hook.Hook[*RecordConfirmPasswordResetEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterConfirmPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordConfirmPasswordResetEvent] | ||||
|  | ||||
| 	// OnRecordBeforeRequestVerificationRequest hook is triggered before each Record | ||||
| 	// request verification API request (after request data load and before sending the verification email). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the loaded request data or implement | ||||
| 	// completely different verification behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeRequestVerificationRequest() *hook.Hook[*RecordRequestVerificationEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeRequestVerificationRequest(tags ...string) *hook.TaggedHook[*RecordRequestVerificationEvent] | ||||
|  | ||||
| 	// OnRecordAfterRequestVerificationRequest hook is triggered after each | ||||
| 	// successful request verification API request. | ||||
| 	OnRecordAfterRequestVerificationRequest() *hook.Hook[*RecordRequestVerificationEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterRequestVerificationRequest(tags ...string) *hook.TaggedHook[*RecordRequestVerificationEvent] | ||||
|  | ||||
| 	// OnRecordBeforeConfirmVerificationRequest hook is triggered before each Record | ||||
| 	// confirm verification API request (after request data load and before persistence). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different persistence behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeConfirmVerificationRequest() *hook.Hook[*RecordConfirmVerificationEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeConfirmVerificationRequest(tags ...string) *hook.TaggedHook[*RecordConfirmVerificationEvent] | ||||
|  | ||||
| 	// OnRecordAfterConfirmVerificationRequest hook is triggered after each | ||||
| 	// successful confirm verification API request. | ||||
| 	OnRecordAfterConfirmVerificationRequest() *hook.Hook[*RecordConfirmVerificationEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterConfirmVerificationRequest(tags ...string) *hook.TaggedHook[*RecordConfirmVerificationEvent] | ||||
|  | ||||
| 	// OnRecordBeforeRequestEmailChangeRequest hook is triggered before each Record request email change API request | ||||
| 	// (after request data load and before sending the email link to confirm the change). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different request email change behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeRequestEmailChangeRequest() *hook.Hook[*RecordRequestEmailChangeEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeRequestEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordRequestEmailChangeEvent] | ||||
|  | ||||
| 	// OnRecordAfterRequestEmailChangeRequest hook is triggered after each | ||||
| 	// successful request email change API request. | ||||
| 	OnRecordAfterRequestEmailChangeRequest() *hook.Hook[*RecordRequestEmailChangeEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterRequestEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordRequestEmailChangeEvent] | ||||
|  | ||||
| 	// OnRecordBeforeConfirmEmailChangeRequest hook is triggered before each Record | ||||
| 	// confirm email change API request (after request data load and before persistence). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different persistence behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeConfirmEmailChangeRequest() *hook.Hook[*RecordConfirmEmailChangeEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeConfirmEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordConfirmEmailChangeEvent] | ||||
|  | ||||
| 	// OnRecordAfterConfirmEmailChangeRequest hook is triggered after each | ||||
| 	// successful confirm email change API request. | ||||
| 	OnRecordAfterConfirmEmailChangeRequest() *hook.Hook[*RecordConfirmEmailChangeEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterConfirmEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordConfirmEmailChangeEvent] | ||||
|  | ||||
| 	// OnRecordListExternalAuthsRequest hook is triggered on each API record external auths list request. | ||||
| 	// | ||||
| 	// Could be used to validate or modify the response before returning it to the client. | ||||
| 	OnRecordListExternalAuthsRequest() *hook.Hook[*RecordListExternalAuthsEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordListExternalAuthsRequest(tags ...string) *hook.TaggedHook[*RecordListExternalAuthsEvent] | ||||
|  | ||||
| 	// OnRecordBeforeUnlinkExternalAuthRequest hook is triggered before each API record | ||||
| 	// external auth unlink request (after models load and before the actual relation deletion). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different delete behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeUnlinkExternalAuthRequest(tags ...string) *hook.TaggedHook[*RecordUnlinkExternalAuthEvent] | ||||
|  | ||||
| 	// OnRecordAfterUnlinkExternalAuthRequest hook is triggered after each | ||||
| 	// successful API record external auth unlink request. | ||||
| 	OnRecordAfterUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterUnlinkExternalAuthRequest(tags ...string) *hook.TaggedHook[*RecordUnlinkExternalAuthEvent] | ||||
|  | ||||
| 	// --------------------------------------------------------------- | ||||
| 	// Record CRUD API event hooks | ||||
| @@ -493,45 +601,69 @@ type App interface { | ||||
| 	// OnRecordsListRequest hook is triggered on each API Records list request. | ||||
| 	// | ||||
| 	// Could be used to validate or modify the response before returning it to the client. | ||||
| 	OnRecordsListRequest() *hook.Hook[*RecordsListEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordsListRequest(tags ...string) *hook.TaggedHook[*RecordsListEvent] | ||||
|  | ||||
| 	// OnRecordViewRequest hook is triggered on each API Record view request. | ||||
| 	// | ||||
| 	// Could be used to validate or modify the response before returning it to the client. | ||||
| 	OnRecordViewRequest() *hook.Hook[*RecordViewEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordViewRequest(tags ...string) *hook.TaggedHook[*RecordViewEvent] | ||||
|  | ||||
| 	// OnRecordBeforeCreateRequest hook is triggered before each API Record | ||||
| 	// create request (after request data load and before model persistence). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different persistence behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeCreateRequest() *hook.Hook[*RecordCreateEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeCreateRequest(tags ...string) *hook.TaggedHook[*RecordCreateEvent] | ||||
|  | ||||
| 	// OnRecordAfterCreateRequest hook is triggered after each | ||||
| 	// successful API Record create request. | ||||
| 	OnRecordAfterCreateRequest() *hook.Hook[*RecordCreateEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterCreateRequest(tags ...string) *hook.TaggedHook[*RecordCreateEvent] | ||||
|  | ||||
| 	// OnRecordBeforeUpdateRequest hook is triggered before each API Record | ||||
| 	// update request (after request data load and before model persistence). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different persistence behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeUpdateRequest() *hook.Hook[*RecordUpdateEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeUpdateRequest(tags ...string) *hook.TaggedHook[*RecordUpdateEvent] | ||||
|  | ||||
| 	// OnRecordAfterUpdateRequest hook is triggered after each | ||||
| 	// successful API Record update request. | ||||
| 	OnRecordAfterUpdateRequest() *hook.Hook[*RecordUpdateEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterUpdateRequest(tags ...string) *hook.TaggedHook[*RecordUpdateEvent] | ||||
|  | ||||
| 	// OnRecordBeforeDeleteRequest hook is triggered before each API Record | ||||
| 	// delete request (after model load and before actual deletion). | ||||
| 	// | ||||
| 	// Could be used to additionally validate the request data or implement | ||||
| 	// completely different delete behavior (returning [hook.StopPropagation]). | ||||
| 	OnRecordBeforeDeleteRequest() *hook.Hook[*RecordDeleteEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordBeforeDeleteRequest(tags ...string) *hook.TaggedHook[*RecordDeleteEvent] | ||||
|  | ||||
| 	// OnRecordAfterDeleteRequest hook is triggered after each | ||||
| 	// successful API Record delete request. | ||||
| 	OnRecordAfterDeleteRequest() *hook.Hook[*RecordDeleteEvent] | ||||
| 	// | ||||
| 	// You can optionally specify a list of "tags" (Collection ids or names) | ||||
| 	// to filter any newly attached event data handler. | ||||
| 	OnRecordAfterDeleteRequest(tags ...string) *hook.TaggedHook[*RecordDeleteEvent] | ||||
|  | ||||
| 	// --------------------------------------------------------------- | ||||
| 	// Collection API event hooks | ||||
|   | ||||
							
								
								
									
										172
									
								
								core/base.go
									
									
									
									
									
								
							
							
						
						
									
										172
									
								
								core/base.go
									
									
									
									
									
								
							| @@ -541,28 +541,28 @@ func (app *BaseApp) OnAfterApiError() *hook.Hook[*ApiErrorEvent] { | ||||
| // Dao event hooks | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| func (app *BaseApp) OnModelBeforeCreate() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelBeforeCreate | ||||
| func (app *BaseApp) OnModelBeforeCreate(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelBeforeCreate, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnModelAfterCreate() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelAfterCreate | ||||
| func (app *BaseApp) OnModelAfterCreate(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelAfterCreate, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnModelBeforeUpdate() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelBeforeUpdate | ||||
| func (app *BaseApp) OnModelBeforeUpdate(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelBeforeUpdate, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnModelAfterUpdate() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelAfterUpdate | ||||
| func (app *BaseApp) OnModelAfterUpdate(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelAfterUpdate, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnModelBeforeDelete() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelBeforeDelete | ||||
| func (app *BaseApp) OnModelBeforeDelete(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelBeforeDelete, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnModelAfterDelete() *hook.Hook[*ModelEvent] { | ||||
| 	return app.onModelAfterDelete | ||||
| func (app *BaseApp) OnModelAfterDelete(tags ...string) *hook.TaggedHook[*ModelEvent] { | ||||
| 	return hook.NewTaggedHook(app.onModelAfterDelete, tags...) | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| @@ -577,28 +577,28 @@ func (app *BaseApp) OnMailerAfterAdminResetPasswordSend() *hook.Hook[*MailerAdmi | ||||
| 	return app.onMailerAfterAdminResetPasswordSend | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerBeforeRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerBeforeRecordResetPasswordSend | ||||
| func (app *BaseApp) OnMailerBeforeRecordResetPasswordSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerBeforeRecordResetPasswordSend, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerAfterRecordResetPasswordSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerAfterRecordResetPasswordSend | ||||
| func (app *BaseApp) OnMailerAfterRecordResetPasswordSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerAfterRecordResetPasswordSend, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerBeforeRecordVerificationSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerBeforeRecordVerificationSend | ||||
| func (app *BaseApp) OnMailerBeforeRecordVerificationSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerBeforeRecordVerificationSend, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerAfterRecordVerificationSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerAfterRecordVerificationSend | ||||
| func (app *BaseApp) OnMailerAfterRecordVerificationSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerAfterRecordVerificationSend, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerBeforeRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerBeforeRecordChangeEmailSend | ||||
| func (app *BaseApp) OnMailerBeforeRecordChangeEmailSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerBeforeRecordChangeEmailSend, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnMailerAfterRecordChangeEmailSend() *hook.Hook[*MailerRecordEvent] { | ||||
| 	return app.onMailerAfterRecordChangeEmailSend | ||||
| func (app *BaseApp) OnMailerAfterRecordChangeEmailSend(tags ...string) *hook.TaggedHook[*MailerRecordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onMailerAfterRecordChangeEmailSend, tags...) | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| @@ -649,8 +649,8 @@ func (app *BaseApp) OnSettingsAfterUpdateRequest() *hook.Hook[*SettingsUpdateEve | ||||
| // File API event hooks | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| func (app *BaseApp) OnFileDownloadRequest() *hook.Hook[*FileDownloadEvent] { | ||||
| 	return app.onFileDownloadRequest | ||||
| func (app *BaseApp) OnFileDownloadRequest(tags ...string) *hook.TaggedHook[*FileDownloadEvent] { | ||||
| 	return hook.NewTaggedHook(app.onFileDownloadRequest, tags...) | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| @@ -729,128 +729,128 @@ func (app *BaseApp) OnAdminAfterConfirmPasswordResetRequest() *hook.Hook[*AdminC | ||||
| // Record auth API event hooks | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| func (app *BaseApp) OnRecordAuthRequest() *hook.Hook[*RecordAuthEvent] { | ||||
| 	return app.onRecordAuthRequest | ||||
| func (app *BaseApp) OnRecordAuthRequest(tags ...string) *hook.TaggedHook[*RecordAuthEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAuthRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeAuthWithPasswordRequest() *hook.Hook[*RecordAuthWithPasswordEvent] { | ||||
| 	return app.onRecordBeforeAuthWithPasswordRequest | ||||
| func (app *BaseApp) OnRecordBeforeAuthWithPasswordRequest(tags ...string) *hook.TaggedHook[*RecordAuthWithPasswordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeAuthWithPasswordRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterAuthWithPasswordRequest() *hook.Hook[*RecordAuthWithPasswordEvent] { | ||||
| 	return app.onRecordAfterAuthWithPasswordRequest | ||||
| func (app *BaseApp) OnRecordAfterAuthWithPasswordRequest(tags ...string) *hook.TaggedHook[*RecordAuthWithPasswordEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterAuthWithPasswordRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeAuthWithOAuth2Request() *hook.Hook[*RecordAuthWithOAuth2Event] { | ||||
| 	return app.onRecordBeforeAuthWithOAuth2Request | ||||
| func (app *BaseApp) OnRecordBeforeAuthWithOAuth2Request(tags ...string) *hook.TaggedHook[*RecordAuthWithOAuth2Event] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeAuthWithOAuth2Request, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterAuthWithOAuth2Request() *hook.Hook[*RecordAuthWithOAuth2Event] { | ||||
| 	return app.onRecordAfterAuthWithOAuth2Request | ||||
| func (app *BaseApp) OnRecordAfterAuthWithOAuth2Request(tags ...string) *hook.TaggedHook[*RecordAuthWithOAuth2Event] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterAuthWithOAuth2Request, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeAuthRefreshRequest() *hook.Hook[*RecordAuthRefreshEvent] { | ||||
| 	return app.onRecordBeforeAuthRefreshRequest | ||||
| func (app *BaseApp) OnRecordBeforeAuthRefreshRequest(tags ...string) *hook.TaggedHook[*RecordAuthRefreshEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeAuthRefreshRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterAuthRefreshRequest() *hook.Hook[*RecordAuthRefreshEvent] { | ||||
| 	return app.onRecordAfterAuthRefreshRequest | ||||
| func (app *BaseApp) OnRecordAfterAuthRefreshRequest(tags ...string) *hook.TaggedHook[*RecordAuthRefreshEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterAuthRefreshRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeRequestPasswordResetRequest() *hook.Hook[*RecordRequestPasswordResetEvent] { | ||||
| 	return app.onRecordBeforeRequestPasswordResetRequest | ||||
| func (app *BaseApp) OnRecordBeforeRequestPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordRequestPasswordResetEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeRequestPasswordResetRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterRequestPasswordResetRequest() *hook.Hook[*RecordRequestPasswordResetEvent] { | ||||
| 	return app.onRecordAfterRequestPasswordResetRequest | ||||
| func (app *BaseApp) OnRecordAfterRequestPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordRequestPasswordResetEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterRequestPasswordResetRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeConfirmPasswordResetRequest() *hook.Hook[*RecordConfirmPasswordResetEvent] { | ||||
| 	return app.onRecordBeforeConfirmPasswordResetRequest | ||||
| func (app *BaseApp) OnRecordBeforeConfirmPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordConfirmPasswordResetEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeConfirmPasswordResetRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterConfirmPasswordResetRequest() *hook.Hook[*RecordConfirmPasswordResetEvent] { | ||||
| 	return app.onRecordAfterConfirmPasswordResetRequest | ||||
| func (app *BaseApp) OnRecordAfterConfirmPasswordResetRequest(tags ...string) *hook.TaggedHook[*RecordConfirmPasswordResetEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterConfirmPasswordResetRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeRequestVerificationRequest() *hook.Hook[*RecordRequestVerificationEvent] { | ||||
| 	return app.onRecordBeforeRequestVerificationRequest | ||||
| func (app *BaseApp) OnRecordBeforeRequestVerificationRequest(tags ...string) *hook.TaggedHook[*RecordRequestVerificationEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeRequestVerificationRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterRequestVerificationRequest() *hook.Hook[*RecordRequestVerificationEvent] { | ||||
| 	return app.onRecordAfterRequestVerificationRequest | ||||
| func (app *BaseApp) OnRecordAfterRequestVerificationRequest(tags ...string) *hook.TaggedHook[*RecordRequestVerificationEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterRequestVerificationRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeConfirmVerificationRequest() *hook.Hook[*RecordConfirmVerificationEvent] { | ||||
| 	return app.onRecordBeforeConfirmVerificationRequest | ||||
| func (app *BaseApp) OnRecordBeforeConfirmVerificationRequest(tags ...string) *hook.TaggedHook[*RecordConfirmVerificationEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeConfirmVerificationRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterConfirmVerificationRequest() *hook.Hook[*RecordConfirmVerificationEvent] { | ||||
| 	return app.onRecordAfterConfirmVerificationRequest | ||||
| func (app *BaseApp) OnRecordAfterConfirmVerificationRequest(tags ...string) *hook.TaggedHook[*RecordConfirmVerificationEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterConfirmVerificationRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeRequestEmailChangeRequest() *hook.Hook[*RecordRequestEmailChangeEvent] { | ||||
| 	return app.onRecordBeforeRequestEmailChangeRequest | ||||
| func (app *BaseApp) OnRecordBeforeRequestEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordRequestEmailChangeEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeRequestEmailChangeRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterRequestEmailChangeRequest() *hook.Hook[*RecordRequestEmailChangeEvent] { | ||||
| 	return app.onRecordAfterRequestEmailChangeRequest | ||||
| func (app *BaseApp) OnRecordAfterRequestEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordRequestEmailChangeEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterRequestEmailChangeRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeConfirmEmailChangeRequest() *hook.Hook[*RecordConfirmEmailChangeEvent] { | ||||
| 	return app.onRecordBeforeConfirmEmailChangeRequest | ||||
| func (app *BaseApp) OnRecordBeforeConfirmEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordConfirmEmailChangeEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeConfirmEmailChangeRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterConfirmEmailChangeRequest() *hook.Hook[*RecordConfirmEmailChangeEvent] { | ||||
| 	return app.onRecordAfterConfirmEmailChangeRequest | ||||
| func (app *BaseApp) OnRecordAfterConfirmEmailChangeRequest(tags ...string) *hook.TaggedHook[*RecordConfirmEmailChangeEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterConfirmEmailChangeRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordListExternalAuthsRequest() *hook.Hook[*RecordListExternalAuthsEvent] { | ||||
| 	return app.onRecordListExternalAuthsRequest | ||||
| func (app *BaseApp) OnRecordListExternalAuthsRequest(tags ...string) *hook.TaggedHook[*RecordListExternalAuthsEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordListExternalAuthsRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] { | ||||
| 	return app.onRecordBeforeUnlinkExternalAuthRequest | ||||
| func (app *BaseApp) OnRecordBeforeUnlinkExternalAuthRequest(tags ...string) *hook.TaggedHook[*RecordUnlinkExternalAuthEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeUnlinkExternalAuthRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterUnlinkExternalAuthRequest() *hook.Hook[*RecordUnlinkExternalAuthEvent] { | ||||
| 	return app.onRecordAfterUnlinkExternalAuthRequest | ||||
| func (app *BaseApp) OnRecordAfterUnlinkExternalAuthRequest(tags ...string) *hook.TaggedHook[*RecordUnlinkExternalAuthEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterUnlinkExternalAuthRequest, tags...) | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| // Record CRUD API event hooks | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| func (app *BaseApp) OnRecordsListRequest() *hook.Hook[*RecordsListEvent] { | ||||
| 	return app.onRecordsListRequest | ||||
| func (app *BaseApp) OnRecordsListRequest(tags ...string) *hook.TaggedHook[*RecordsListEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordsListRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordViewRequest() *hook.Hook[*RecordViewEvent] { | ||||
| 	return app.onRecordViewRequest | ||||
| func (app *BaseApp) OnRecordViewRequest(tags ...string) *hook.TaggedHook[*RecordViewEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordViewRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeCreateRequest() *hook.Hook[*RecordCreateEvent] { | ||||
| 	return app.onRecordBeforeCreateRequest | ||||
| func (app *BaseApp) OnRecordBeforeCreateRequest(tags ...string) *hook.TaggedHook[*RecordCreateEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeCreateRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterCreateRequest() *hook.Hook[*RecordCreateEvent] { | ||||
| 	return app.onRecordAfterCreateRequest | ||||
| func (app *BaseApp) OnRecordAfterCreateRequest(tags ...string) *hook.TaggedHook[*RecordCreateEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterCreateRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeUpdateRequest() *hook.Hook[*RecordUpdateEvent] { | ||||
| 	return app.onRecordBeforeUpdateRequest | ||||
| func (app *BaseApp) OnRecordBeforeUpdateRequest(tags ...string) *hook.TaggedHook[*RecordUpdateEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeUpdateRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterUpdateRequest() *hook.Hook[*RecordUpdateEvent] { | ||||
| 	return app.onRecordAfterUpdateRequest | ||||
| func (app *BaseApp) OnRecordAfterUpdateRequest(tags ...string) *hook.TaggedHook[*RecordUpdateEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterUpdateRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordBeforeDeleteRequest() *hook.Hook[*RecordDeleteEvent] { | ||||
| 	return app.onRecordBeforeDeleteRequest | ||||
| func (app *BaseApp) OnRecordBeforeDeleteRequest(tags ...string) *hook.TaggedHook[*RecordDeleteEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordBeforeDeleteRequest, tags...) | ||||
| } | ||||
|  | ||||
| func (app *BaseApp) OnRecordAfterDeleteRequest() *hook.Hook[*RecordDeleteEvent] { | ||||
| 	return app.onRecordAfterDeleteRequest | ||||
| func (app *BaseApp) OnRecordAfterDeleteRequest(tags ...string) *hook.TaggedHook[*RecordDeleteEvent] { | ||||
| 	return hook.NewTaggedHook(app.onRecordAfterDeleteRequest, tags...) | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
|   | ||||
| @@ -182,238 +182,6 @@ func TestBaseAppGetters(t *testing.T) { | ||||
| 	if app.onBeforeServe != app.OnBeforeServe() || app.OnBeforeServe() == nil { | ||||
| 		t.Fatalf("Getter app.OnBeforeServe does not match or nil (%v vs %v)", app.OnBeforeServe(), app.onBeforeServe) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelBeforeCreate != app.OnModelBeforeCreate() || app.OnModelBeforeCreate() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelBeforeCreate does not match or nil (%v vs %v)", app.OnModelBeforeCreate(), app.onModelBeforeCreate) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelAfterCreate != app.OnModelAfterCreate() || app.OnModelAfterCreate() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelAfterCreate does not match or nil (%v vs %v)", app.OnModelAfterCreate(), app.onModelAfterCreate) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelBeforeUpdate != app.OnModelBeforeUpdate() || app.OnModelBeforeUpdate() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelBeforeUpdate does not match or nil (%v vs %v)", app.OnModelBeforeUpdate(), app.onModelBeforeUpdate) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelAfterUpdate != app.OnModelAfterUpdate() || app.OnModelAfterUpdate() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelAfterUpdate does not match or nil (%v vs %v)", app.OnModelAfterUpdate(), app.onModelAfterUpdate) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelBeforeDelete != app.OnModelBeforeDelete() || app.OnModelBeforeDelete() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelBeforeDelete does not match or nil (%v vs %v)", app.OnModelBeforeDelete(), app.onModelBeforeDelete) | ||||
| 	} | ||||
|  | ||||
| 	if app.onModelAfterDelete != app.OnModelAfterDelete() || app.OnModelAfterDelete() == nil { | ||||
| 		t.Fatalf("Getter app.OnModelAfterDelete does not match or nil (%v vs %v)", app.OnModelAfterDelete(), app.onModelAfterDelete) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerBeforeAdminResetPasswordSend != app.OnMailerBeforeAdminResetPasswordSend() || app.OnMailerBeforeAdminResetPasswordSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerBeforeAdminResetPasswordSend does not match or nil (%v vs %v)", app.OnMailerBeforeAdminResetPasswordSend(), app.onMailerBeforeAdminResetPasswordSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerAfterAdminResetPasswordSend != app.OnMailerAfterAdminResetPasswordSend() || app.OnMailerAfterAdminResetPasswordSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerAfterAdminResetPasswordSend does not match or nil (%v vs %v)", app.OnMailerAfterAdminResetPasswordSend(), app.onMailerAfterAdminResetPasswordSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerBeforeRecordResetPasswordSend != app.OnMailerBeforeRecordResetPasswordSend() || app.OnMailerBeforeRecordResetPasswordSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerBeforeRecordResetPasswordSend does not match or nil (%v vs %v)", app.OnMailerBeforeRecordResetPasswordSend(), app.onMailerBeforeRecordResetPasswordSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerAfterRecordResetPasswordSend != app.OnMailerAfterRecordResetPasswordSend() || app.OnMailerAfterRecordResetPasswordSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerAfterRecordResetPasswordSend does not match or nil (%v vs %v)", app.OnMailerAfterRecordResetPasswordSend(), app.onMailerAfterRecordResetPasswordSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerBeforeRecordVerificationSend != app.OnMailerBeforeRecordVerificationSend() || app.OnMailerBeforeRecordVerificationSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerBeforeRecordVerificationSend does not match or nil (%v vs %v)", app.OnMailerBeforeRecordVerificationSend(), app.onMailerBeforeRecordVerificationSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerAfterRecordVerificationSend != app.OnMailerAfterRecordVerificationSend() || app.OnMailerAfterRecordVerificationSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerAfterRecordVerificationSend does not match or nil (%v vs %v)", app.OnMailerAfterRecordVerificationSend(), app.onMailerAfterRecordVerificationSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerBeforeRecordChangeEmailSend != app.OnMailerBeforeRecordChangeEmailSend() || app.OnMailerBeforeRecordChangeEmailSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerBeforeRecordChangeEmailSend does not match or nil (%v vs %v)", app.OnMailerBeforeRecordChangeEmailSend(), app.onMailerBeforeRecordChangeEmailSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onMailerAfterRecordChangeEmailSend != app.OnMailerAfterRecordChangeEmailSend() || app.OnMailerAfterRecordChangeEmailSend() == nil { | ||||
| 		t.Fatalf("Getter app.OnMailerAfterRecordChangeEmailSend does not match or nil (%v vs %v)", app.OnMailerAfterRecordChangeEmailSend(), app.onMailerAfterRecordChangeEmailSend) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRealtimeConnectRequest != app.OnRealtimeConnectRequest() || app.OnRealtimeConnectRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRealtimeConnectRequest does not match or nil (%v vs %v)", app.OnRealtimeConnectRequest(), app.onRealtimeConnectRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRealtimeBeforeSubscribeRequest != app.OnRealtimeBeforeSubscribeRequest() || app.OnRealtimeBeforeSubscribeRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRealtimeBeforeSubscribeRequest does not match or nil (%v vs %v)", app.OnRealtimeBeforeSubscribeRequest(), app.onRealtimeBeforeSubscribeRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRealtimeAfterSubscribeRequest != app.OnRealtimeAfterSubscribeRequest() || app.OnRealtimeAfterSubscribeRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRealtimeAfterSubscribeRequest does not match or nil (%v vs %v)", app.OnRealtimeAfterSubscribeRequest(), app.onRealtimeAfterSubscribeRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onSettingsListRequest != app.OnSettingsListRequest() || app.OnSettingsListRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnSettingsListRequest does not match or nil (%v vs %v)", app.OnSettingsListRequest(), app.onSettingsListRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onSettingsBeforeUpdateRequest != app.OnSettingsBeforeUpdateRequest() || app.OnSettingsBeforeUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnSettingsBeforeUpdateRequest does not match or nil (%v vs %v)", app.OnSettingsBeforeUpdateRequest(), app.onSettingsBeforeUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onSettingsAfterUpdateRequest != app.OnSettingsAfterUpdateRequest() || app.OnSettingsAfterUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnSettingsAfterUpdateRequest does not match or nil (%v vs %v)", app.OnSettingsAfterUpdateRequest(), app.onSettingsAfterUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onFileDownloadRequest != app.OnFileDownloadRequest() || app.OnFileDownloadRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnFileDownloadRequest does not match or nil (%v vs %v)", app.OnFileDownloadRequest(), app.onFileDownloadRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminsListRequest != app.OnAdminsListRequest() || app.OnAdminsListRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminsListRequest does not match or nil (%v vs %v)", app.OnAdminsListRequest(), app.onAdminsListRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminViewRequest != app.OnAdminViewRequest() || app.OnAdminViewRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminViewRequest does not match or nil (%v vs %v)", app.OnAdminViewRequest(), app.onAdminViewRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminBeforeCreateRequest != app.OnAdminBeforeCreateRequest() || app.OnAdminBeforeCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminBeforeCreateRequest does not match or nil (%v vs %v)", app.OnAdminBeforeCreateRequest(), app.onAdminBeforeCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminAfterCreateRequest != app.OnAdminAfterCreateRequest() || app.OnAdminAfterCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminAfterCreateRequest does not match or nil (%v vs %v)", app.OnAdminAfterCreateRequest(), app.onAdminAfterCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminBeforeUpdateRequest != app.OnAdminBeforeUpdateRequest() || app.OnAdminBeforeUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminBeforeUpdateRequest does not match or nil (%v vs %v)", app.OnAdminBeforeUpdateRequest(), app.onAdminBeforeUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminAfterUpdateRequest != app.OnAdminAfterUpdateRequest() || app.OnAdminAfterUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminAfterUpdateRequest does not match or nil (%v vs %v)", app.OnAdminAfterUpdateRequest(), app.onAdminAfterUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminBeforeDeleteRequest != app.OnAdminBeforeDeleteRequest() || app.OnAdminBeforeDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminBeforeDeleteRequest does not match or nil (%v vs %v)", app.OnAdminBeforeDeleteRequest(), app.onAdminBeforeDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminAfterDeleteRequest != app.OnAdminAfterDeleteRequest() || app.OnAdminAfterDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminAfterDeleteRequest does not match or nil (%v vs %v)", app.OnAdminAfterDeleteRequest(), app.onAdminAfterDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onAdminAuthRequest != app.OnAdminAuthRequest() || app.OnAdminAuthRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnAdminAuthRequest does not match or nil (%v vs %v)", app.OnAdminAuthRequest(), app.onAdminAuthRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordsListRequest != app.OnRecordsListRequest() || app.OnRecordsListRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordsListRequest does not match or nil (%v vs %v)", app.OnRecordsListRequest(), app.onRecordsListRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordViewRequest != app.OnRecordViewRequest() || app.OnRecordViewRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordViewRequest does not match or nil (%v vs %v)", app.OnRecordViewRequest(), app.onRecordViewRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeCreateRequest != app.OnRecordBeforeCreateRequest() || app.OnRecordBeforeCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeCreateRequest does not match or nil (%v vs %v)", app.OnRecordBeforeCreateRequest(), app.onRecordBeforeCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterCreateRequest != app.OnRecordAfterCreateRequest() || app.OnRecordAfterCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterCreateRequest does not match or nil (%v vs %v)", app.OnRecordAfterCreateRequest(), app.onRecordAfterCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeUpdateRequest != app.OnRecordBeforeUpdateRequest() || app.OnRecordBeforeUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeUpdateRequest does not match or nil (%v vs %v)", app.OnRecordBeforeUpdateRequest(), app.onRecordBeforeUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterUpdateRequest != app.OnRecordAfterUpdateRequest() || app.OnRecordAfterUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterUpdateRequest does not match or nil (%v vs %v)", app.OnRecordAfterUpdateRequest(), app.onRecordAfterUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeDeleteRequest != app.OnRecordBeforeDeleteRequest() || app.OnRecordBeforeDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeDeleteRequest does not match or nil (%v vs %v)", app.OnRecordBeforeDeleteRequest(), app.onRecordBeforeDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterDeleteRequest != app.OnRecordAfterDeleteRequest() || app.OnRecordAfterDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterDeleteRequest does not match or nil (%v vs %v)", app.OnRecordAfterDeleteRequest(), app.onRecordAfterDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAuthRequest != app.OnRecordAuthRequest() || app.OnRecordAuthRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAuthRequest does not match or nil (%v vs %v)", app.OnRecordAuthRequest(), app.onRecordAuthRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordListExternalAuthsRequest != app.OnRecordListExternalAuthsRequest() || app.OnRecordListExternalAuthsRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordListExternalAuthsRequest does not match or nil (%v vs %v)", app.OnRecordListExternalAuthsRequest(), app.onRecordListExternalAuthsRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeUnlinkExternalAuthRequest != app.OnRecordBeforeUnlinkExternalAuthRequest() || app.OnRecordBeforeUnlinkExternalAuthRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeUnlinkExternalAuthRequest does not match or nil (%v vs %v)", app.OnRecordBeforeUnlinkExternalAuthRequest(), app.onRecordBeforeUnlinkExternalAuthRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterUnlinkExternalAuthRequest != app.OnRecordAfterUnlinkExternalAuthRequest() || app.OnRecordAfterUnlinkExternalAuthRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterUnlinkExternalAuthRequest does not match or nil (%v vs %v)", app.OnRecordAfterUnlinkExternalAuthRequest(), app.onRecordAfterUnlinkExternalAuthRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordsListRequest != app.OnRecordsListRequest() || app.OnRecordsListRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordsListRequest does not match or nil (%v vs %v)", app.OnRecordsListRequest(), app.onRecordsListRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordViewRequest != app.OnRecordViewRequest() || app.OnRecordViewRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordViewRequest does not match or nil (%v vs %v)", app.OnRecordViewRequest(), app.onRecordViewRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeCreateRequest != app.OnRecordBeforeCreateRequest() || app.OnRecordBeforeCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeCreateRequest does not match or nil (%v vs %v)", app.OnRecordBeforeCreateRequest(), app.onRecordBeforeCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterCreateRequest != app.OnRecordAfterCreateRequest() || app.OnRecordAfterCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterCreateRequest does not match or nil (%v vs %v)", app.OnRecordAfterCreateRequest(), app.onRecordAfterCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeUpdateRequest != app.OnRecordBeforeUpdateRequest() || app.OnRecordBeforeUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeUpdateRequest does not match or nil (%v vs %v)", app.OnRecordBeforeUpdateRequest(), app.onRecordBeforeUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterUpdateRequest != app.OnRecordAfterUpdateRequest() || app.OnRecordAfterUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterUpdateRequest does not match or nil (%v vs %v)", app.OnRecordAfterUpdateRequest(), app.onRecordAfterUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordBeforeDeleteRequest != app.OnRecordBeforeDeleteRequest() || app.OnRecordBeforeDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordBeforeDeleteRequest does not match or nil (%v vs %v)", app.OnRecordBeforeDeleteRequest(), app.onRecordBeforeDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onRecordAfterDeleteRequest != app.OnRecordAfterDeleteRequest() || app.OnRecordAfterDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnRecordAfterDeleteRequest does not match or nil (%v vs %v)", app.OnRecordAfterDeleteRequest(), app.onRecordAfterDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionsListRequest != app.OnCollectionsListRequest() || app.OnCollectionsListRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionsListRequest does not match or nil (%v vs %v)", app.OnCollectionsListRequest(), app.onCollectionsListRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionViewRequest != app.OnCollectionViewRequest() || app.OnCollectionViewRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionViewRequest does not match or nil (%v vs %v)", app.OnCollectionViewRequest(), app.onCollectionViewRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionBeforeCreateRequest != app.OnCollectionBeforeCreateRequest() || app.OnCollectionBeforeCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionBeforeCreateRequest does not match or nil (%v vs %v)", app.OnCollectionBeforeCreateRequest(), app.onCollectionBeforeCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionAfterCreateRequest != app.OnCollectionAfterCreateRequest() || app.OnCollectionAfterCreateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionAfterCreateRequest does not match or nil (%v vs %v)", app.OnCollectionAfterCreateRequest(), app.onCollectionAfterCreateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionBeforeUpdateRequest != app.OnCollectionBeforeUpdateRequest() || app.OnCollectionBeforeUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionBeforeUpdateRequest does not match or nil (%v vs %v)", app.OnCollectionBeforeUpdateRequest(), app.onCollectionBeforeUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionAfterUpdateRequest != app.OnCollectionAfterUpdateRequest() || app.OnCollectionAfterUpdateRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionAfterUpdateRequest does not match or nil (%v vs %v)", app.OnCollectionAfterUpdateRequest(), app.onCollectionAfterUpdateRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionBeforeDeleteRequest != app.OnCollectionBeforeDeleteRequest() || app.OnCollectionBeforeDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionBeforeDeleteRequest does not match or nil (%v vs %v)", app.OnCollectionBeforeDeleteRequest(), app.onCollectionBeforeDeleteRequest) | ||||
| 	} | ||||
|  | ||||
| 	if app.onCollectionAfterDeleteRequest != app.OnCollectionAfterDeleteRequest() || app.OnCollectionAfterDeleteRequest() == nil { | ||||
| 		t.Fatalf("Getter app.OnCollectionAfterDeleteRequest does not match or nil (%v vs %v)", app.OnCollectionAfterDeleteRequest(), app.onCollectionAfterDeleteRequest) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestBaseAppNewMailClient(t *testing.T) { | ||||
|   | ||||
| @@ -11,7 +11,7 @@ func initPragmas(db *dbx.DB) error { | ||||
| 	_, err := db.NewQuery(` | ||||
| 		PRAGMA busy_timeout       = 10000; | ||||
| 		PRAGMA journal_mode       = WAL; | ||||
| 		PRAGMA journal_size_limit = 100000000; | ||||
| 		PRAGMA journal_size_limit = 200000000; | ||||
| 		PRAGMA synchronous        = NORMAL; | ||||
| 		PRAGMA foreign_keys       = TRUE; | ||||
| 	`).Execute() | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import ( | ||||
| 	"github.com/pocketbase/pocketbase/models/schema" | ||||
| 	"github.com/pocketbase/pocketbase/models/settings" | ||||
| 	"github.com/pocketbase/pocketbase/tools/auth" | ||||
| 	"github.com/pocketbase/pocketbase/tools/hook" | ||||
| 	"github.com/pocketbase/pocketbase/tools/mailer" | ||||
| 	"github.com/pocketbase/pocketbase/tools/search" | ||||
| 	"github.com/pocketbase/pocketbase/tools/subscriptions" | ||||
| @@ -13,6 +14,28 @@ import ( | ||||
| 	"github.com/labstack/echo/v5" | ||||
| ) | ||||
|  | ||||
| type BaseCollectionEvent struct { | ||||
| 	Collection *models.Collection | ||||
| } | ||||
|  | ||||
| func (e *BaseCollectionEvent) Tags() []string { | ||||
| 	if e.Collection == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	tags := make([]string, 0, 2) | ||||
|  | ||||
| 	if e.Collection.Id != "" { | ||||
| 		tags = append(tags, e.Collection.Id) | ||||
| 	} | ||||
|  | ||||
| 	if e.Collection.Name != "" { | ||||
| 		tags = append(tags, e.Collection.Name) | ||||
| 	} | ||||
|  | ||||
| 	return tags | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| // Serve events data | ||||
| // ------------------------------------------------------------------- | ||||
| @@ -35,16 +58,32 @@ type ApiErrorEvent struct { | ||||
| // Model DAO events data | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| var _ hook.Tagger = (*ModelEvent)(nil) | ||||
|  | ||||
| type ModelEvent struct { | ||||
| 	Dao   *daos.Dao | ||||
| 	Model models.Model | ||||
| } | ||||
|  | ||||
| func (e *ModelEvent) Tags() []string { | ||||
| 	if e.Model == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	if r, ok := e.Model.(*models.Record); ok && r.Collection() != nil { | ||||
| 		return []string{r.Collection().Id, r.Collection().Name} | ||||
| 	} | ||||
|  | ||||
| 	return []string{e.Model.TableName()} | ||||
| } | ||||
|  | ||||
| // ------------------------------------------------------------------- | ||||
| // Mailer events data | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| type MailerRecordEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	MailClient mailer.Mailer | ||||
| 	Message    *mailer.Message | ||||
| 	Record     *models.Record | ||||
| @@ -104,28 +143,37 @@ type SettingsUpdateEvent struct { | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| type RecordsListEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| 	Records     []*models.Record | ||||
| 	Result      *search.Result | ||||
| } | ||||
|  | ||||
| type RecordViewEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordCreateEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordUpdateEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordDeleteEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
| @@ -135,6 +183,8 @@ type RecordDeleteEvent struct { | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| type RecordAuthEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| 	Token       string | ||||
| @@ -142,6 +192,8 @@ type RecordAuthEvent struct { | ||||
| } | ||||
|  | ||||
| type RecordAuthWithPasswordEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| 	Identity    string | ||||
| @@ -149,53 +201,73 @@ type RecordAuthWithPasswordEvent struct { | ||||
| } | ||||
|  | ||||
| type RecordAuthWithOAuth2Event struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| 	OAuth2User  *auth.AuthUser | ||||
| } | ||||
|  | ||||
| type RecordAuthRefreshEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordRequestPasswordResetEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordConfirmPasswordResetEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordRequestVerificationEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordConfirmVerificationEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordRequestEmailChangeEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordConfirmEmailChangeEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Record      *models.Record | ||||
| } | ||||
|  | ||||
| type RecordListExternalAuthsEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext   echo.Context | ||||
| 	Record        *models.Record | ||||
| 	ExternalAuths []*models.ExternalAuth | ||||
| } | ||||
|  | ||||
| type RecordUnlinkExternalAuthEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext  echo.Context | ||||
| 	Record       *models.Record | ||||
| 	ExternalAuth *models.ExternalAuth | ||||
| @@ -270,23 +342,27 @@ type CollectionsListEvent struct { | ||||
| } | ||||
|  | ||||
| type CollectionViewEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| } | ||||
|  | ||||
| type CollectionCreateEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| } | ||||
|  | ||||
| type CollectionUpdateEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| } | ||||
|  | ||||
| type CollectionDeleteEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| } | ||||
|  | ||||
| type CollectionsImportEvent struct { | ||||
| @@ -299,8 +375,9 @@ type CollectionsImportEvent struct { | ||||
| // ------------------------------------------------------------------- | ||||
|  | ||||
| type FileDownloadEvent struct { | ||||
| 	BaseCollectionEvent | ||||
|  | ||||
| 	HttpContext echo.Context | ||||
| 	Collection  *models.Collection | ||||
| 	Record      *models.Record | ||||
| 	FileField   *schema.SchemaField | ||||
| 	ServedPath  string | ||||
|   | ||||
							
								
								
									
										84
									
								
								core/events_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								core/events_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| package core_test | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/pocketbase/pocketbase/core" | ||||
| 	"github.com/pocketbase/pocketbase/models" | ||||
| 	"github.com/pocketbase/pocketbase/tools/list" | ||||
| ) | ||||
|  | ||||
| func TestBaseCollectionEventTags(t *testing.T) { | ||||
| 	c1 := new(models.Collection) | ||||
|  | ||||
| 	c2 := new(models.Collection) | ||||
| 	c2.Id = "a" | ||||
|  | ||||
| 	c3 := new(models.Collection) | ||||
| 	c3.Name = "b" | ||||
|  | ||||
| 	c4 := new(models.Collection) | ||||
| 	c4.Id = "a" | ||||
| 	c4.Name = "b" | ||||
|  | ||||
| 	scenarios := []struct { | ||||
| 		collection   *models.Collection | ||||
| 		expectedTags []string | ||||
| 	}{ | ||||
| 		{c1, []string{}}, | ||||
| 		{c2, []string{"a"}}, | ||||
| 		{c3, []string{"b"}}, | ||||
| 		{c4, []string{"a", "b"}}, | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range scenarios { | ||||
| 		event := new(core.BaseCollectionEvent) | ||||
| 		event.Collection = s.collection | ||||
|  | ||||
| 		tags := event.Tags() | ||||
|  | ||||
| 		if len(s.expectedTags) != len(tags) { | ||||
| 			t.Fatalf("[%d] Expected %v tags, got %v", i, s.expectedTags, tags) | ||||
| 		} | ||||
|  | ||||
| 		for _, tag := range s.expectedTags { | ||||
| 			if !list.ExistInSlice(tag, tags) { | ||||
| 				t.Fatalf("[%d] Expected %v tags, got %v", i, s.expectedTags, tags) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestModelEventTags(t *testing.T) { | ||||
| 	m1 := new(models.Admin) | ||||
|  | ||||
| 	c := new(models.Collection) | ||||
| 	c.Id = "a" | ||||
| 	c.Name = "b" | ||||
| 	m2 := models.NewRecord(c) | ||||
|  | ||||
| 	scenarios := []struct { | ||||
| 		model        models.Model | ||||
| 		expectedTags []string | ||||
| 	}{ | ||||
| 		{m1, []string{"_admins"}}, | ||||
| 		{m2, []string{"a", "b"}}, | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range scenarios { | ||||
| 		event := new(core.ModelEvent) | ||||
| 		event.Model = s.model | ||||
|  | ||||
| 		tags := event.Tags() | ||||
|  | ||||
| 		if len(s.expectedTags) != len(tags) { | ||||
| 			t.Fatalf("[%d] Expected %v tags, got %v", i, s.expectedTags, tags) | ||||
| 		} | ||||
|  | ||||
| 		for _, tag := range s.expectedTags { | ||||
| 			if !list.ExistInSlice(tag, tags) { | ||||
| 				t.Fatalf("[%d] Expected %v tags, got %v", i, s.expectedTags, tags) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -61,12 +61,11 @@ func SendAdminPasswordReset(app core.App, admin *models.Admin) error { | ||||
| 		HTML:    body, | ||||
| 	} | ||||
|  | ||||
| 	event := &core.MailerAdminEvent{ | ||||
| 		MailClient: mailClient, | ||||
| 		Message:    message, | ||||
| 		Admin:      admin, | ||||
| 		Meta:       map[string]any{"token": token}, | ||||
| 	} | ||||
| 	event := new(core.MailerAdminEvent) | ||||
| 	event.MailClient = mailClient | ||||
| 	event.Message = message | ||||
| 	event.Admin = admin | ||||
| 	event.Meta = map[string]any{"token": token} | ||||
|  | ||||
| 	sendErr := app.OnMailerBeforeAdminResetPasswordSend().Trigger(event, func(e *core.MailerAdminEvent) error { | ||||
| 		return e.MailClient.Send(e.Message) | ||||
|   | ||||
| @@ -37,12 +37,12 @@ func SendRecordPasswordReset(app core.App, authRecord *models.Record) error { | ||||
| 		HTML:    body, | ||||
| 	} | ||||
|  | ||||
| 	event := &core.MailerRecordEvent{ | ||||
| 		MailClient: mailClient, | ||||
| 		Message:    message, | ||||
| 		Record:     authRecord, | ||||
| 		Meta:       map[string]any{"token": token}, | ||||
| 	} | ||||
| 	event := new(core.MailerRecordEvent) | ||||
| 	event.MailClient = mailClient | ||||
| 	event.Message = message | ||||
| 	event.Collection = authRecord.Collection() | ||||
| 	event.Record = authRecord | ||||
| 	event.Meta = map[string]any{"token": token} | ||||
|  | ||||
| 	sendErr := app.OnMailerBeforeRecordResetPasswordSend().Trigger(event, func(e *core.MailerRecordEvent) error { | ||||
| 		return e.MailClient.Send(e.Message) | ||||
| @@ -81,12 +81,12 @@ func SendRecordVerification(app core.App, authRecord *models.Record) error { | ||||
| 		HTML:    body, | ||||
| 	} | ||||
|  | ||||
| 	event := &core.MailerRecordEvent{ | ||||
| 		MailClient: mailClient, | ||||
| 		Message:    message, | ||||
| 		Record:     authRecord, | ||||
| 		Meta:       map[string]any{"token": token}, | ||||
| 	} | ||||
| 	event := new(core.MailerRecordEvent) | ||||
| 	event.MailClient = mailClient | ||||
| 	event.Message = message | ||||
| 	event.Collection = authRecord.Collection() | ||||
| 	event.Record = authRecord | ||||
| 	event.Meta = map[string]any{"token": token} | ||||
|  | ||||
| 	sendErr := app.OnMailerBeforeRecordVerificationSend().Trigger(event, func(e *core.MailerRecordEvent) error { | ||||
| 		return e.MailClient.Send(e.Message) | ||||
| @@ -125,14 +125,14 @@ func SendRecordChangeEmail(app core.App, record *models.Record, newEmail string) | ||||
| 		HTML:    body, | ||||
| 	} | ||||
|  | ||||
| 	event := &core.MailerRecordEvent{ | ||||
| 		MailClient: mailClient, | ||||
| 		Message:    message, | ||||
| 		Record:     record, | ||||
| 		Meta: map[string]any{ | ||||
| 			"token":    token, | ||||
| 			"newEmail": newEmail, | ||||
| 		}, | ||||
| 	event := new(core.MailerRecordEvent) | ||||
| 	event.MailClient = mailClient | ||||
| 	event.Message = message | ||||
| 	event.Collection = record.Collection() | ||||
| 	event.Record = record | ||||
| 	event.Meta = map[string]any{ | ||||
| 		"token":    token, | ||||
| 		"newEmail": newEmail, | ||||
| 	} | ||||
|  | ||||
| 	sendErr := app.OnMailerBeforeRecordChangeEmailSend().Trigger(event, func(e *core.MailerRecordEvent) error { | ||||
|   | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestAddAndPreAdd(t *testing.T) { | ||||
| func TestHookAddAndPreAdd(t *testing.T) { | ||||
| 	h := Hook[int]{} | ||||
|  | ||||
| 	if total := len(h.handlers); total != 0 { | ||||
| @@ -36,7 +36,7 @@ func TestAddAndPreAdd(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestReset(t *testing.T) { | ||||
| func TestHookReset(t *testing.T) { | ||||
| 	h := Hook[int]{} | ||||
|  | ||||
| 	h.Reset() // should do nothing and not panic | ||||
| @@ -55,7 +55,7 @@ func TestReset(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestTrigger(t *testing.T) { | ||||
| func TestHookTrigger(t *testing.T) { | ||||
| 	err1 := errors.New("demo") | ||||
| 	err2 := errors.New("demo") | ||||
|  | ||||
| @@ -92,7 +92,7 @@ func TestTrigger(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestTriggerStopPropagation(t *testing.T) { | ||||
| func TestHookTriggerStopPropagation(t *testing.T) { | ||||
| 	called1 := false | ||||
| 	f1 := func(data int) error { called1 = true; return nil } | ||||
|  | ||||
|   | ||||
							
								
								
									
										74
									
								
								tools/hook/tagged.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								tools/hook/tagged.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| package hook | ||||
|  | ||||
| import ( | ||||
| 	"github.com/pocketbase/pocketbase/tools/list" | ||||
| ) | ||||
|  | ||||
| // Tagger defines an interface for event data structs that support tags/groups/categories/etc. | ||||
| // Usually used together with TaggedHook. | ||||
| type Tagger interface { | ||||
| 	Tags() []string | ||||
| } | ||||
|  | ||||
| // wrapped local Hook embedded struct to limit the public API surface. | ||||
| type mainHook[T Tagger] struct { | ||||
| 	*Hook[T] | ||||
| } | ||||
|  | ||||
| // NewTaggedHook creates a new TaggedHook with the provided main hook and optional tags. | ||||
| func NewTaggedHook[T Tagger](hook *Hook[T], tags ...string) *TaggedHook[T] { | ||||
| 	return &TaggedHook[T]{ | ||||
| 		mainHook[T]{hook}, | ||||
| 		tags, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TaggedHook defines a proxy hook which register handlers that are triggered only | ||||
| // if the TaggedHook.tags are empty or includes at least one of the event data tag(s). | ||||
| type TaggedHook[T Tagger] struct { | ||||
| 	mainHook[T] | ||||
|  | ||||
| 	tags []string | ||||
| } | ||||
|  | ||||
| // CanTriggerOn checks if the current TaggedHook can be triggered with | ||||
| // the provided event data tags. | ||||
| func (p *TaggedHook[T]) CanTriggerOn(tags []string) bool { | ||||
| 	if len(p.tags) == 0 { | ||||
| 		return true // match all | ||||
| 	} | ||||
|  | ||||
| 	for _, t := range tags { | ||||
| 		if list.ExistInSlice(t, p.tags) { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // PreAdd registers a new handler to the hook by prepending it to the existing queue. | ||||
| // | ||||
| // The fn handler will be called only if the event data tags satisfy p.CanTriggerOn. | ||||
| func (p *TaggedHook[T]) PreAdd(fn Handler[T]) { | ||||
| 	p.mainHook.PreAdd(func(e T) error { | ||||
| 		if p.CanTriggerOn(e.Tags()) { | ||||
| 			return fn(e) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // Add registers a new handler to the hook by appending it to the existing queue. | ||||
| // | ||||
| // The fn handler will be called only if the event data tags satisfy p.CanTriggerOn. | ||||
| func (p *TaggedHook[T]) Add(fn Handler[T]) { | ||||
| 	p.mainHook.Add(func(e T) error { | ||||
| 		if p.CanTriggerOn(e.Tags()) { | ||||
| 			return fn(e) | ||||
| 		} | ||||
|  | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
							
								
								
									
										69
									
								
								tools/hook/tagged_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								tools/hook/tagged_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| package hook | ||||
|  | ||||
| import "testing" | ||||
|  | ||||
| type mockTagsData struct { | ||||
| 	tags []string | ||||
| } | ||||
|  | ||||
| func (m mockTagsData) Tags() []string { | ||||
| 	return m.tags | ||||
| } | ||||
|  | ||||
| func TestTaggedHook(t *testing.T) { | ||||
| 	triggerSequence := "" | ||||
|  | ||||
| 	base := &Hook[mockTagsData]{} | ||||
| 	base.Add(func(data mockTagsData) error { triggerSequence += "f0"; return nil }) | ||||
|  | ||||
| 	hA := NewTaggedHook(base) | ||||
| 	hA.Add(func(data mockTagsData) error { triggerSequence += "a1"; return nil }) | ||||
| 	hA.PreAdd(func(data mockTagsData) error { triggerSequence += "a2"; return nil }) | ||||
|  | ||||
| 	hB := NewTaggedHook(base, "b1", "b2") | ||||
| 	hB.Add(func(data mockTagsData) error { triggerSequence += "b1"; return nil }) | ||||
| 	hB.PreAdd(func(data mockTagsData) error { triggerSequence += "b2"; return nil }) | ||||
|  | ||||
| 	hC := NewTaggedHook(base, "c1", "c2") | ||||
| 	hC.Add(func(data mockTagsData) error { triggerSequence += "c1"; return nil }) | ||||
| 	hC.PreAdd(func(data mockTagsData) error { triggerSequence += "c2"; return nil }) | ||||
|  | ||||
| 	scenarios := []struct { | ||||
| 		data             mockTagsData | ||||
| 		expectedSequence string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			mockTagsData{}, | ||||
| 			"a2f0a1", | ||||
| 		}, | ||||
| 		{ | ||||
| 			mockTagsData{[]string{"missing"}}, | ||||
| 			"a2f0a1", | ||||
| 		}, | ||||
| 		{ | ||||
| 			mockTagsData{[]string{"b2"}}, | ||||
| 			"b2a2f0a1b1", | ||||
| 		}, | ||||
| 		{ | ||||
| 			mockTagsData{[]string{"c1"}}, | ||||
| 			"c2a2f0a1c1", | ||||
| 		}, | ||||
| 		{ | ||||
| 			mockTagsData{[]string{"b1", "c2"}}, | ||||
| 			"c2b2a2f0a1b1c1", | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for i, s := range scenarios { | ||||
| 		triggerSequence = "" // reset | ||||
|  | ||||
| 		err := hA.Trigger(s.data) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("[%d] Unexpected trigger error: %v", i, err) | ||||
| 		} | ||||
|  | ||||
| 		if triggerSequence != s.expectedSequence { | ||||
| 			t.Fatalf("[%d] Expected trigger sequence %s, got %s", i, s.expectedSequence, triggerSequence) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user