You've already forked pocketbase
							
							
				mirror of
				https://github.com/pocketbase/pocketbase.git
				synced 2025-10-31 08:37:38 +02:00 
			
		
		
		
	[#80] fixed before hooks data and added optional interceptor to upsert submit
This commit is contained in:
		| @@ -166,7 +166,7 @@ func (api *adminApi) create(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminCreateEvent{ | ||||
| @@ -174,20 +174,24 @@ func (api *adminApi) create(c echo.Context) error { | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnAdminBeforeCreateRequest().Trigger(event, func(e *core.AdminCreateEvent) error { | ||||
| 		// create the admin | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to create admin.", err) | ||||
| 		} | ||||
| 	// create the admin | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnAdminBeforeCreateRequest().Trigger(event, func(e *core.AdminCreateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to create admin.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Admin) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Admin) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnAdminAfterCreateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *adminApi) update(c echo.Context) error { | ||||
| @@ -205,7 +209,7 @@ func (api *adminApi) update(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.AdminUpdateEvent{ | ||||
| @@ -213,20 +217,24 @@ func (api *adminApi) update(c echo.Context) error { | ||||
| 		Admin:       admin, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnAdminBeforeUpdateRequest().Trigger(event, func(e *core.AdminUpdateEvent) error { | ||||
| 		// update the admin | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to update admin.", err) | ||||
| 		} | ||||
| 	// update the admin | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnAdminBeforeUpdateRequest().Trigger(event, func(e *core.AdminUpdateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to update admin.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Admin) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Admin) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnAdminAfterUpdateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *adminApi) delete(c echo.Context) error { | ||||
|   | ||||
| @@ -507,9 +507,6 @@ func TestAdminCreate(t *testing.T) { | ||||
| 			}, | ||||
| 			ExpectedStatus:  400, | ||||
| 			ExpectedContent: []string{`"data":{"email":{"code":"validation_required","message":"Cannot be blank."},"password":{"code":"validation_required","message":"Cannot be blank."}}`}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnAdminBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin + invalid data format", | ||||
| @@ -532,9 +529,6 @@ func TestAdminCreate(t *testing.T) { | ||||
| 			}, | ||||
| 			ExpectedStatus:  400, | ||||
| 			ExpectedContent: []string{`"data":{"avatar":{"code":"validation_max_less_equal_than_required","message":"Must be no greater than 9."},"email":{"code":"validation_admin_email_exists","message":"Admin email already exists."},"password":{"code":"validation_length_out_of_range","message":"The length must be between 10 and 100."},"passwordConfirm":{"code":"validation_values_mismatch","message":"Values don't match."}}`}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnAdminBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin + valid data", | ||||
| @@ -647,9 +641,6 @@ func TestAdminUpdate(t *testing.T) { | ||||
| 			}, | ||||
| 			ExpectedStatus:  400, | ||||
| 			ExpectedContent: []string{`"data":{"avatar":{"code":"validation_max_less_equal_than_required","message":"Must be no greater than 9."},"email":{"code":"validation_admin_email_exists","message":"Admin email already exists."},"password":{"code":"validation_length_out_of_range","message":"The length must be between 10 and 100."},"passwordConfirm":{"code":"validation_values_mismatch","message":"Values don't match."}}`}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnAdminBeforeUpdateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Method: http.MethodPatch, | ||||
|   | ||||
| @@ -76,9 +76,9 @@ func (api *collectionApi) create(c echo.Context) error { | ||||
|  | ||||
| 	form := forms.NewCollectionUpsert(api.app, collection) | ||||
|  | ||||
| 	// read | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionCreateEvent{ | ||||
| @@ -86,20 +86,24 @@ func (api *collectionApi) create(c echo.Context) error { | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnCollectionBeforeCreateRequest().Trigger(event, func(e *core.CollectionCreateEvent) error { | ||||
| 		// submit | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to create the collection.", err) | ||||
| 		} | ||||
| 	// create the collection | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnCollectionBeforeCreateRequest().Trigger(event, func(e *core.CollectionCreateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to create the collection.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Collection) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Collection) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnCollectionAfterCreateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *collectionApi) update(c echo.Context) error { | ||||
| @@ -110,9 +114,9 @@ func (api *collectionApi) update(c echo.Context) error { | ||||
|  | ||||
| 	form := forms.NewCollectionUpsert(api.app, collection) | ||||
|  | ||||
| 	// read | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.CollectionUpdateEvent{ | ||||
| @@ -120,20 +124,24 @@ func (api *collectionApi) update(c echo.Context) error { | ||||
| 		Collection:  collection, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnCollectionBeforeUpdateRequest().Trigger(event, func(e *core.CollectionUpdateEvent) error { | ||||
| 		// submit | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to update the collection.", err) | ||||
| 		} | ||||
| 	// update the collection | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnCollectionBeforeUpdateRequest().Trigger(event, func(e *core.CollectionUpdateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to update the collection.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Collection) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Collection) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnCollectionAfterUpdateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *collectionApi) delete(c echo.Context) error { | ||||
|   | ||||
| @@ -297,9 +297,6 @@ func TestCollectionCreate(t *testing.T) { | ||||
| 				`"name":{"code":"validation_required"`, | ||||
| 				`"schema":{"code":"validation_required"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnCollectionBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin + invalid data (eg. existing name)", | ||||
| @@ -315,9 +312,6 @@ func TestCollectionCreate(t *testing.T) { | ||||
| 				`"name":{"code":"validation_collection_name_exists"`, | ||||
| 				`"schema":{"0":{"name":{"code":"validation_required"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnCollectionBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin + valid data", | ||||
| @@ -399,9 +393,6 @@ func TestCollectionUpdate(t *testing.T) { | ||||
| 				`"data":{`, | ||||
| 				`"name":{"code":"validation_collection_name_exists"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnCollectionBeforeUpdateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin + valid data", | ||||
|   | ||||
| @@ -193,7 +193,7 @@ func (api *recordApi) create(c echo.Context) error { | ||||
| 		testRecord := models.NewRecord(collection) | ||||
| 		testForm := forms.NewRecordUpsert(api.app, testRecord) | ||||
| 		if err := testForm.LoadData(c.Request()); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 			return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 		} | ||||
|  | ||||
| 		testErr := testForm.DrySubmit(func(txDao *daos.Dao) error { | ||||
| @@ -210,7 +210,7 @@ func (api *recordApi) create(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := form.LoadData(c.Request()); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordCreateEvent{ | ||||
| @@ -218,20 +218,24 @@ func (api *recordApi) create(c echo.Context) error { | ||||
| 		Record:      record, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnRecordBeforeCreateRequest().Trigger(event, func(e *core.RecordCreateEvent) error { | ||||
| 		// create the record | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to create record.", err) | ||||
| 		} | ||||
| 	// create the record | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnRecordBeforeCreateRequest().Trigger(event, func(e *core.RecordCreateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to create record.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Record) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Record) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnRecordAfterCreateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *recordApi) update(c echo.Context) error { | ||||
| @@ -276,7 +280,7 @@ func (api *recordApi) update(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := form.LoadData(c.Request()); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.RecordUpdateEvent{ | ||||
| @@ -284,20 +288,24 @@ func (api *recordApi) update(c echo.Context) error { | ||||
| 		Record:      record, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnRecordBeforeUpdateRequest().Trigger(event, func(e *core.RecordUpdateEvent) error { | ||||
| 		// update the record | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to update record.", err) | ||||
| 		} | ||||
| 	// update the record | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnRecordBeforeUpdateRequest().Trigger(event, func(e *core.RecordUpdateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to update record.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.Record) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.Record) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnRecordAfterUpdateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *recordApi) delete(c echo.Context) error { | ||||
|   | ||||
| @@ -40,6 +40,8 @@ func (api *settingsApi) list(c echo.Context) error { | ||||
|  | ||||
| func (api *settingsApi) set(c echo.Context) error { | ||||
| 	form := forms.NewSettingsUpsert(api.app) | ||||
|  | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("An error occurred while reading the submitted data.", err) | ||||
| 	} | ||||
| @@ -50,22 +52,27 @@ func (api *settingsApi) set(c echo.Context) error { | ||||
| 		NewSettings: form.Settings, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnSettingsBeforeUpdateRequest().Trigger(event, func(e *core.SettingsUpdateEvent) error { | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("An error occurred while submitting the form.", err) | ||||
| 		} | ||||
| 	// update the settings | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnSettingsBeforeUpdateRequest().Trigger(event, func(e *core.SettingsUpdateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("An error occurred while submitting the form.", err) | ||||
| 				} | ||||
|  | ||||
| 		redactedSettings, err := api.app.Settings().RedactClone() | ||||
| 		if err != nil { | ||||
| 			return rest.NewBadRequestError("", err) | ||||
| 		} | ||||
| 				redactedSettings, err := api.app.Settings().RedactClone() | ||||
| 				if err != nil { | ||||
| 					return rest.NewBadRequestError("", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, redactedSettings) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, redactedSettings) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnSettingsAfterUpdateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|   | ||||
| @@ -139,9 +139,6 @@ func TestSettingsSet(t *testing.T) { | ||||
| 				`"emailAuth":{"minPasswordLength":{"code":"validation_min_greater_equal_than_required","message":"Must be no less than 5."}}`, | ||||
| 				`"meta":{"appName":{"code":"validation_required","message":"Cannot be blank."}}`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnSettingsBeforeUpdateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin submitting valid data", | ||||
|   | ||||
							
								
								
									
										44
									
								
								apis/user.go
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								apis/user.go
									
									
									
									
									
								
							| @@ -348,7 +348,7 @@ func (api *userApi) create(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.UserCreateEvent{ | ||||
| @@ -356,20 +356,24 @@ func (api *userApi) create(c echo.Context) error { | ||||
| 		User:        user, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnUserBeforeCreateRequest().Trigger(event, func(e *core.UserCreateEvent) error { | ||||
| 		// create the user | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to create user.", err) | ||||
| 		} | ||||
| 	// create the user | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnUserBeforeCreateRequest().Trigger(event, func(e *core.UserCreateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to create user.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.User) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.User) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnUserAfterCreateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *userApi) update(c echo.Context) error { | ||||
| @@ -387,7 +391,7 @@ func (api *userApi) update(c echo.Context) error { | ||||
|  | ||||
| 	// load request | ||||
| 	if err := c.Bind(form); err != nil { | ||||
| 		return rest.NewBadRequestError("Failed to read the submitted data due to invalid formatting.", err) | ||||
| 		return rest.NewBadRequestError("Failed to load the submitted data due to invalid formatting.", err) | ||||
| 	} | ||||
|  | ||||
| 	event := &core.UserUpdateEvent{ | ||||
| @@ -395,20 +399,24 @@ func (api *userApi) update(c echo.Context) error { | ||||
| 		User:        user, | ||||
| 	} | ||||
|  | ||||
| 	handlerErr := api.app.OnUserBeforeUpdateRequest().Trigger(event, func(e *core.UserUpdateEvent) error { | ||||
| 		// update the user | ||||
| 		if err := form.Submit(); err != nil { | ||||
| 			return rest.NewBadRequestError("Failed to update user.", err) | ||||
| 		} | ||||
| 	// update the user | ||||
| 	submitErr := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			return api.app.OnUserBeforeUpdateRequest().Trigger(event, func(e *core.UserUpdateEvent) error { | ||||
| 				if err := next(); err != nil { | ||||
| 					return rest.NewBadRequestError("Failed to update user.", err) | ||||
| 				} | ||||
|  | ||||
| 		return e.HttpContext.JSON(http.StatusOK, e.User) | ||||
| 				return e.HttpContext.JSON(http.StatusOK, e.User) | ||||
| 			}) | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	if handlerErr == nil { | ||||
| 	if submitErr == nil { | ||||
| 		api.app.OnUserAfterUpdateRequest().Trigger(event) | ||||
| 	} | ||||
|  | ||||
| 	return handlerErr | ||||
| 	return submitErr | ||||
| } | ||||
|  | ||||
| func (api *userApi) delete(c echo.Context) error { | ||||
|   | ||||
| @@ -748,9 +748,6 @@ func TestUserCreate(t *testing.T) { | ||||
| 				`"email":{"code":"validation_required"`, | ||||
| 				`"password":{"code":"validation_required"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnUserBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:           "invalid data", | ||||
| @@ -764,9 +761,6 @@ func TestUserCreate(t *testing.T) { | ||||
| 				`"password":{"code":"validation_length_out_of_range"`, | ||||
| 				`"passwordConfirm":{"code":"validation_values_mismatch"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnUserBeforeCreateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "valid data but with disabled email/pass auth", | ||||
| @@ -868,9 +862,6 @@ func TestUserUpdate(t *testing.T) { | ||||
| 				`"data":{`, | ||||
| 				`"email":{"code":"validation_user_email_exists"`, | ||||
| 			}, | ||||
| 			ExpectedEvents: map[string]int{ | ||||
| 				"OnUserBeforeUpdateRequest": 1, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:   "authorized as admin - valid data", | ||||
|   | ||||
| @@ -74,8 +74,11 @@ func (form *AdminUpsert) checkUniqueEmail(value any) error { | ||||
| 	return validation.NewError("validation_admin_email_exists", "Admin email already exists.") | ||||
| } | ||||
|  | ||||
| // Submit validates the form and upserts the form's admin model. | ||||
| func (form *AdminUpsert) Submit() error { | ||||
| // Submit validates the form and upserts the form admin model. | ||||
| // | ||||
| // You can optionally provide a list of InterceptorFunc to further | ||||
| // modify the form behavior before persisting it. | ||||
| func (form *AdminUpsert) Submit(interceptors ...InterceptorFunc) error { | ||||
| 	if err := form.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -87,5 +90,7 @@ func (form *AdminUpsert) Submit() error { | ||||
| 		form.admin.SetPassword(form.Password) | ||||
| 	} | ||||
|  | ||||
| 	return form.app.Dao().SaveAdmin(form.admin) | ||||
| 	return runInterceptors(func() error { | ||||
| 		return form.app.Dao().SaveAdmin(form.admin) | ||||
| 	}, interceptors...) | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package forms_test | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	validation "github.com/go-ozzo/ozzo-validation/v4" | ||||
| @@ -252,7 +253,14 @@ func TestAdminUpsertSubmit(t *testing.T) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		err := form.Submit() | ||||
| 		interceptorCalls := 0 | ||||
|  | ||||
| 		err := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 			return func() error { | ||||
| 				interceptorCalls++ | ||||
| 				return next() | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		hasErr := err != nil | ||||
| 		if hasErr != s.expectError { | ||||
| @@ -266,6 +274,14 @@ func TestAdminUpsertSubmit(t *testing.T) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		expectInterceptorCall := 1 | ||||
| 		if s.expectError { | ||||
| 			expectInterceptorCall = 0 | ||||
| 		} | ||||
| 		if interceptorCalls != expectInterceptorCall { | ||||
| 			t.Errorf("(%d) Expected interceptor to be called %d, got %d", i, expectInterceptorCall, interceptorCalls) | ||||
| 		} | ||||
|  | ||||
| 		if s.expectError { | ||||
| 			continue // skip persistence check | ||||
| 		} | ||||
| @@ -283,3 +299,51 @@ func TestAdminUpsertSubmit(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestAdminUpsertSubmitInterceptors(t *testing.T) { | ||||
| 	app, _ := tests.NewTestApp() | ||||
| 	defer app.Cleanup() | ||||
|  | ||||
| 	admin := &models.Admin{} | ||||
| 	form := forms.NewAdminUpsert(app, admin) | ||||
| 	form.Email = "test_new@example.com" | ||||
| 	form.Password = "1234567890" | ||||
| 	form.PasswordConfirm = form.Password | ||||
|  | ||||
| 	testErr := errors.New("test_error") | ||||
| 	interceptorAdminEmail := "" | ||||
|  | ||||
| 	interceptor1Called := false | ||||
| 	interceptor1 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor1Called = true | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interceptor2Called := false | ||||
| 	interceptor2 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorAdminEmail = admin.Email // to check if the record was filled | ||||
| 			interceptor2Called = true | ||||
| 			return testErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err := form.Submit(interceptor1, interceptor2) | ||||
| 	if err != testErr { | ||||
| 		t.Fatalf("Expected error %v, got %v", testErr, err) | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor1Called { | ||||
| 		t.Fatalf("Expected interceptor1 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor2Called { | ||||
| 		t.Fatalf("Expected interceptor2 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if interceptorAdminEmail != form.Email { | ||||
| 		t.Fatalf("Expected the form model to be filled before calling the interceptors") | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										19
									
								
								forms/base.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								forms/base.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| // Package models implements various services used for request data | ||||
| // validation and applying changes to existing DB models through the app Dao. | ||||
| package forms | ||||
|  | ||||
| // InterceptorNextFunc is a interceptor handler function. | ||||
| // Usually used in combination with InterceptorFunc. | ||||
| type InterceptorNextFunc = func() error | ||||
|  | ||||
| // InterceptorFunc defines a single interceptor function that will execute the provided next func handler. | ||||
| type InterceptorFunc func(next InterceptorNextFunc) InterceptorNextFunc | ||||
|  | ||||
| // runInterceptors executes the provided list of interceptors. | ||||
| func runInterceptors(next InterceptorNextFunc, interceptors ...InterceptorFunc) error { | ||||
| 	for i := len(interceptors) - 1; i >= 0; i-- { | ||||
| 		next = interceptors[i](next) | ||||
| 	} | ||||
|  | ||||
| 	return next() | ||||
| } | ||||
| @@ -189,7 +189,10 @@ func (form *CollectionUpsert) checkRule(value any) error { | ||||
| // Submit validates the form and upserts the form's Collection model. | ||||
| // | ||||
| // On success the related record table schema will be auto updated. | ||||
| func (form *CollectionUpsert) Submit() error { | ||||
| // | ||||
| // You can optionally provide a list of InterceptorFunc to further | ||||
| // modify the form behavior before persisting it. | ||||
| func (form *CollectionUpsert) Submit(interceptors ...InterceptorFunc) error { | ||||
| 	if err := form.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -211,5 +214,7 @@ func (form *CollectionUpsert) Submit() error { | ||||
| 	form.collection.UpdateRule = form.UpdateRule | ||||
| 	form.collection.DeleteRule = form.DeleteRule | ||||
|  | ||||
| 	return form.app.Dao().SaveCollection(form.collection) | ||||
| 	return runInterceptors(func() error { | ||||
| 		return form.app.Dao().SaveCollection(form.collection) | ||||
| 	}, interceptors...) | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package forms_test | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	validation "github.com/go-ozzo/ozzo-validation/v4" | ||||
| @@ -387,14 +388,31 @@ func TestCollectionUpsertSubmit(t *testing.T) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		interceptorCalls := 0 | ||||
| 		interceptor := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 			return func() error { | ||||
| 				interceptorCalls++ | ||||
| 				return next() | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// parse errors | ||||
| 		result := form.Submit() | ||||
| 		result := form.Submit(interceptor) | ||||
| 		errs, ok := result.(validation.Errors) | ||||
| 		if !ok && result != nil { | ||||
| 			t.Errorf("(%d) Failed to parse errors %v", i, result) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// check interceptor calls | ||||
| 		expectInterceptorCall := 1 | ||||
| 		if len(s.expectedErrors) > 0 { | ||||
| 			expectInterceptorCall = 0 | ||||
| 		} | ||||
| 		if interceptorCalls != expectInterceptorCall { | ||||
| 			t.Errorf("(%d) Expected interceptor to be called %d, got %d", i, expectInterceptorCall, interceptorCalls) | ||||
| 		} | ||||
|  | ||||
| 		// check errors | ||||
| 		if len(errs) > len(s.expectedErrors) { | ||||
| 			t.Errorf("(%d) Expected error keys %v, got %v", i, s.expectedErrors, errs) | ||||
| @@ -450,3 +468,53 @@ func TestCollectionUpsertSubmit(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestCollectionUpsertSubmitInterceptors(t *testing.T) { | ||||
| 	app, _ := tests.NewTestApp() | ||||
| 	defer app.Cleanup() | ||||
|  | ||||
| 	collection, err := app.Dao().FindCollectionByNameOrId("demo") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	form := forms.NewCollectionUpsert(app, collection) | ||||
| 	form.Name = "test_new" | ||||
|  | ||||
| 	testErr := errors.New("test_error") | ||||
| 	interceptorCollectionName := "" | ||||
|  | ||||
| 	interceptor1Called := false | ||||
| 	interceptor1 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor1Called = true | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interceptor2Called := false | ||||
| 	interceptor2 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorCollectionName = collection.Name // to check if the record was filled | ||||
| 			interceptor2Called = true | ||||
| 			return testErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	submitErr := form.Submit(interceptor1, interceptor2) | ||||
| 	if submitErr != testErr { | ||||
| 		t.Fatalf("Expected submitError %v, got %v", testErr, submitErr) | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor1Called { | ||||
| 		t.Fatalf("Expected interceptor1 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor2Called { | ||||
| 		t.Fatalf("Expected interceptor2 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if interceptorCollectionName != form.Name { | ||||
| 		t.Fatalf("Expected the form model to be filled before calling the interceptors") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -271,7 +271,10 @@ func (form *RecordUpsert) DrySubmit(callback func(txDao *daos.Dao) error) error | ||||
| } | ||||
|  | ||||
| // Submit validates the form and upserts the form Record model. | ||||
| func (form *RecordUpsert) Submit() error { | ||||
| // | ||||
| // You can optionally provide a list of InterceptorFunc to further | ||||
| // modify the form behavior before persisting it. | ||||
| func (form *RecordUpsert) Submit(interceptors ...InterceptorFunc) error { | ||||
| 	if err := form.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -281,25 +284,27 @@ func (form *RecordUpsert) Submit() error { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error { | ||||
| 		// persist record model | ||||
| 		if err := txDao.SaveRecord(form.record); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	return runInterceptors(func() error { | ||||
| 		return form.app.Dao().RunInTransaction(func(txDao *daos.Dao) error { | ||||
| 			// persist record model | ||||
| 			if err := txDao.SaveRecord(form.record); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 		// upload new files (if any) | ||||
| 		if err := form.processFilesToUpload(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 			// upload new files (if any) | ||||
| 			if err := form.processFilesToUpload(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 		// delete old files (if any) | ||||
| 		if err := form.processFilesToDelete(); err != nil { //nolint:staticcheck | ||||
| 			// for now fail silently to avoid reupload when `form.Submit()` | ||||
| 			// is called manually (aka. not from an api request)... | ||||
| 		} | ||||
| 			// delete old files (if any) | ||||
| 			if err := form.processFilesToDelete(); err != nil { //nolint:staticcheck | ||||
| 				// for now fail silently to avoid reupload when `form.Submit()` | ||||
| 				// is called manually (aka. not from an api request)... | ||||
| 			} | ||||
|  | ||||
| 		return nil | ||||
| 	}) | ||||
| 			return nil | ||||
| 		}) | ||||
| 	}, interceptors...) | ||||
| } | ||||
|  | ||||
| func (form *RecordUpsert) processFilesToUpload() error { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package forms_test | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
| 	"path/filepath" | ||||
| @@ -400,13 +401,27 @@ func TestRecordUpsertSubmitFailure(t *testing.T) { | ||||
| 	req.Header.Set(echo.HeaderContentType, mp.FormDataContentType()) | ||||
| 	form.LoadData(req) | ||||
|  | ||||
| 	interceptorCalls := 0 | ||||
| 	interceptor := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorCalls++ | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// ensure that validate is triggered | ||||
| 	// --- | ||||
| 	result := form.Submit() | ||||
| 	result := form.Submit(interceptor) | ||||
| 	if result == nil { | ||||
| 		t.Fatal("Expected error, got nil") | ||||
| 	} | ||||
|  | ||||
| 	// check interceptor calls | ||||
| 	// --- | ||||
| 	if interceptorCalls != 0 { | ||||
| 		t.Fatalf("Expected interceptor to be called 0 times, got %d", interceptorCalls) | ||||
| 	} | ||||
|  | ||||
| 	// ensure that the record changes weren't persisted | ||||
| 	// --- | ||||
| 	recordAfter, err := app.Dao().FindFirstRecordByData(collection, "id", recordBefore.Id) | ||||
| @@ -451,11 +466,25 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) { | ||||
| 	req.Header.Set(echo.HeaderContentType, mp.FormDataContentType()) | ||||
| 	form.LoadData(req) | ||||
|  | ||||
| 	result := form.Submit() | ||||
| 	interceptorCalls := 0 | ||||
| 	interceptor := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorCalls++ | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	result := form.Submit(interceptor) | ||||
| 	if result != nil { | ||||
| 		t.Fatalf("Expected nil, got error %v", result) | ||||
| 	} | ||||
|  | ||||
| 	// check interceptor calls | ||||
| 	// --- | ||||
| 	if interceptorCalls != 1 { | ||||
| 		t.Fatalf("Expected interceptor to be called 1 time, got %d", interceptorCalls) | ||||
| 	} | ||||
|  | ||||
| 	// ensure that the record changes were persisted | ||||
| 	// --- | ||||
| 	recordAfter, err := app.Dao().FindFirstRecordByData(collection, "id", recordBefore.Id) | ||||
| @@ -482,6 +511,57 @@ func TestRecordUpsertSubmitSuccess(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestRecordUpsertSubmitInterceptors(t *testing.T) { | ||||
| 	app, _ := tests.NewTestApp() | ||||
| 	defer app.Cleanup() | ||||
|  | ||||
| 	collection, _ := app.Dao().FindCollectionByNameOrId("demo4") | ||||
| 	record, err := app.Dao().FindFirstRecordByData(collection, "id", "054f9f24-0a0a-4e09-87b1-bc7ff2b336a2") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
|  | ||||
| 	form := forms.NewRecordUpsert(app, record) | ||||
| 	form.Data["title"] = "test_new" | ||||
|  | ||||
| 	testErr := errors.New("test_error") | ||||
| 	interceptorRecordTitle := "" | ||||
|  | ||||
| 	interceptor1Called := false | ||||
| 	interceptor1 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor1Called = true | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interceptor2Called := false | ||||
| 	interceptor2 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorRecordTitle = record.GetStringDataValue("title") // to check if the record was filled | ||||
| 			interceptor2Called = true | ||||
| 			return testErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	submitErr := form.Submit(interceptor1, interceptor2) | ||||
| 	if submitErr != testErr { | ||||
| 		t.Fatalf("Expected submitError %v, got %v", testErr, submitErr) | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor1Called { | ||||
| 		t.Fatalf("Expected interceptor1 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor2Called { | ||||
| 		t.Fatalf("Expected interceptor2 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if interceptorRecordTitle != form.Data["title"].(string) { | ||||
| 		t.Fatalf("Expected the form model to be filled before calling the interceptors") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func hasRecordFile(app core.App, record *models.Record, filename string) bool { | ||||
| 	fs, _ := app.NewFilesystem() | ||||
| 	defer fs.Close() | ||||
|   | ||||
| @@ -33,27 +33,32 @@ func (form *SettingsUpsert) Validate() error { | ||||
| // Submit validates the form and upserts the loaded settings. | ||||
| // | ||||
| // On success the app settings will be refreshed with the form ones. | ||||
| func (form *SettingsUpsert) Submit() error { | ||||
| // | ||||
| // You can optionally provide a list of InterceptorFunc to further | ||||
| // modify the form behavior before persisting it. | ||||
| func (form *SettingsUpsert) Submit(interceptors ...InterceptorFunc) error { | ||||
| 	if err := form.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	encryptionKey := os.Getenv(form.app.EncryptionEnv()) | ||||
|  | ||||
| 	saveErr := form.app.Dao().SaveParam( | ||||
| 		models.ParamAppSettings, | ||||
| 		form.Settings, | ||||
| 		encryptionKey, | ||||
| 	) | ||||
| 	if saveErr != nil { | ||||
| 		return saveErr | ||||
| 	} | ||||
| 	return runInterceptors(func() error { | ||||
| 		saveErr := form.app.Dao().SaveParam( | ||||
| 			models.ParamAppSettings, | ||||
| 			form.Settings, | ||||
| 			encryptionKey, | ||||
| 		) | ||||
| 		if saveErr != nil { | ||||
| 			return saveErr | ||||
| 		} | ||||
|  | ||||
| 	// explicitly trigger old logs deletion | ||||
| 	form.app.LogsDao().DeleteOldRequests( | ||||
| 		time.Now().AddDate(0, 0, -1*form.Settings.Logs.MaxDays), | ||||
| 	) | ||||
| 		// explicitly trigger old logs deletion | ||||
| 		form.app.LogsDao().DeleteOldRequests( | ||||
| 			time.Now().AddDate(0, 0, -1*form.Settings.Logs.MaxDays), | ||||
| 		) | ||||
|  | ||||
| 	// merge the application settings with the form ones | ||||
| 	return form.app.Settings().Merge(form.Settings) | ||||
| 		// merge the application settings with the form ones | ||||
| 		return form.app.Settings().Merge(form.Settings) | ||||
| 	}, interceptors...) | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package forms_test | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"os" | ||||
| 	"testing" | ||||
|  | ||||
| @@ -98,14 +99,31 @@ func TestSettingsUpsertSubmit(t *testing.T) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		interceptorCalls := 0 | ||||
| 		interceptor := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 			return func() error { | ||||
| 				interceptorCalls++ | ||||
| 				return next() | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// parse errors | ||||
| 		result := form.Submit() | ||||
| 		result := form.Submit(interceptor) | ||||
| 		errs, ok := result.(validation.Errors) | ||||
| 		if !ok && result != nil { | ||||
| 			t.Errorf("(%d) Failed to parse errors %v", i, result) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// check interceptor calls | ||||
| 		expectInterceptorCall := 1 | ||||
| 		if len(s.expectedErrors) > 0 { | ||||
| 			expectInterceptorCall = 0 | ||||
| 		} | ||||
| 		if interceptorCalls != expectInterceptorCall { | ||||
| 			t.Errorf("(%d) Expected interceptor to be called %d, got %d", i, expectInterceptorCall, interceptorCalls) | ||||
| 		} | ||||
|  | ||||
| 		// check errors | ||||
| 		if len(errs) > len(s.expectedErrors) { | ||||
| 			t.Errorf("(%d) Expected error keys %v, got %v", i, s.expectedErrors, errs) | ||||
| @@ -128,3 +146,42 @@ func TestSettingsUpsertSubmit(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestSettingsUpsertSubmitInterceptors(t *testing.T) { | ||||
| 	app, _ := tests.NewTestApp() | ||||
| 	defer app.Cleanup() | ||||
|  | ||||
| 	form := forms.NewSettingsUpsert(app) | ||||
| 	form.Meta.AppName = "test_new" | ||||
|  | ||||
| 	testErr := errors.New("test_error") | ||||
|  | ||||
| 	interceptor1Called := false | ||||
| 	interceptor1 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor1Called = true | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interceptor2Called := false | ||||
| 	interceptor2 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor2Called = true | ||||
| 			return testErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	submitErr := form.Submit(interceptor1, interceptor2) | ||||
| 	if submitErr != testErr { | ||||
| 		t.Fatalf("Expected submitError %v, got %v", testErr, submitErr) | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor1Called { | ||||
| 		t.Fatalf("Expected interceptor1 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor2Called { | ||||
| 		t.Fatalf("Expected interceptor2 to be called") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -98,7 +98,10 @@ func (form *UserUpsert) checkEmailDomain(value any) error { | ||||
| } | ||||
|  | ||||
| // Submit validates the form and upserts the form user model. | ||||
| func (form *UserUpsert) Submit() error { | ||||
| // | ||||
| // You can optionally provide a list of InterceptorFunc to further | ||||
| // modify the form behavior before persisting it. | ||||
| func (form *UserUpsert) Submit(interceptors ...InterceptorFunc) error { | ||||
| 	if err := form.Validate(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @@ -114,5 +117,7 @@ func (form *UserUpsert) Submit() error { | ||||
|  | ||||
| 	form.user.Email = form.Email | ||||
|  | ||||
| 	return form.app.Dao().SaveUser(form.user) | ||||
| 	return runInterceptors(func() error { | ||||
| 		return form.app.Dao().SaveUser(form.user) | ||||
| 	}, interceptors...) | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package forms_test | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	validation "github.com/go-ozzo/ozzo-validation/v4" | ||||
| @@ -212,13 +213,28 @@ func TestUserUpsertSubmit(t *testing.T) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		err := form.Submit() | ||||
| 		interceptorCalls := 0 | ||||
|  | ||||
| 		err := form.Submit(func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 			return func() error { | ||||
| 				interceptorCalls++ | ||||
| 				return next() | ||||
| 			} | ||||
| 		}) | ||||
|  | ||||
| 		hasErr := err != nil | ||||
| 		if hasErr != s.expectError { | ||||
| 			t.Errorf("(%d) Expected hasErr to be %v, got %v (%v)", i, s.expectError, hasErr, err) | ||||
| 		} | ||||
|  | ||||
| 		expectInterceptorCall := 1 | ||||
| 		if s.expectError { | ||||
| 			expectInterceptorCall = 0 | ||||
| 		} | ||||
| 		if interceptorCalls != expectInterceptorCall { | ||||
| 			t.Errorf("(%d) Expected interceptor to be called %d, got %d", i, expectInterceptorCall, interceptorCalls) | ||||
| 		} | ||||
|  | ||||
| 		if s.expectError { | ||||
| 			continue | ||||
| 		} | ||||
| @@ -240,3 +256,51 @@ func TestUserUpsertSubmit(t *testing.T) { | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestUserUpsertSubmitInterceptors(t *testing.T) { | ||||
| 	app, _ := tests.NewTestApp() | ||||
| 	defer app.Cleanup() | ||||
|  | ||||
| 	user := &models.User{} | ||||
| 	form := forms.NewUserUpsert(app, user) | ||||
| 	form.Email = "test_new@example.com" | ||||
| 	form.Password = "1234567890" | ||||
| 	form.PasswordConfirm = form.Password | ||||
|  | ||||
| 	testErr := errors.New("test_error") | ||||
| 	interceptorUserEmail := "" | ||||
|  | ||||
| 	interceptor1Called := false | ||||
| 	interceptor1 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptor1Called = true | ||||
| 			return next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	interceptor2Called := false | ||||
| 	interceptor2 := func(next forms.InterceptorNextFunc) forms.InterceptorNextFunc { | ||||
| 		return func() error { | ||||
| 			interceptorUserEmail = user.Email // to check if the record was filled | ||||
| 			interceptor2Called = true | ||||
| 			return testErr | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	err := form.Submit(interceptor1, interceptor2) | ||||
| 	if err != testErr { | ||||
| 		t.Fatalf("Expected error %v, got %v", testErr, err) | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor1Called { | ||||
| 		t.Fatalf("Expected interceptor1 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if !interceptor2Called { | ||||
| 		t.Fatalf("Expected interceptor2 to be called") | ||||
| 	} | ||||
|  | ||||
| 	if interceptorUserEmail != form.Email { | ||||
| 		t.Fatalf("Expected the form model to be filled before calling the interceptors") | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user