diff --git a/CHANGELOG.md b/CHANGELOG.md index 613ce441..5c89e7e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ - Added `RequestEvent.Blob(status, contentType string, bytes)` response write helper ([#5940](https://github.com/pocketbase/pocketbase/discussions/5940)). +- Added more descriptive error messages. + ## v0.23.0 diff --git a/apis/record_auth_with_oauth2.go b/apis/record_auth_with_oauth2.go index 9026daa4..1d37e331 100644 --- a/apis/record_auth_with_oauth2.go +++ b/apis/record_auth_with_oauth2.go @@ -175,7 +175,7 @@ type recordOAuth2LoginForm struct { func (form *recordOAuth2LoginForm) validate() error { return validation.ValidateStruct(form, - validation.Field(&form.Provider, validation.Required, validation.By(form.checkProviderName)), + validation.Field(&form.Provider, validation.Required, validation.Length(0, 100), validation.By(form.checkProviderName)), validation.Field(&form.Code, validation.Required), validation.Field(&form.RedirectURL, validation.Required), ) @@ -186,7 +186,7 @@ func (form *recordOAuth2LoginForm) checkProviderName(value any) error { _, ok := form.collection.OAuth2.GetProviderConfig(name) if !ok { - return validation.NewError("validation_invalid_provider", fmt.Sprintf("Provider with name %q is missing or is not enabled.", name)). + return validation.NewError("validation_invalid_provider", "Provider with name {{.name}} is missing or is not enabled."). SetParams(map[string]any{"name": name}) } diff --git a/core/collection_model_auth_options.go b/core/collection_model_auth_options.go index 8dce2d4c..a0198d06 100644 --- a/core/collection_model_auth_options.go +++ b/core/collection_model_auth_options.go @@ -445,7 +445,7 @@ func checkForDuplicatedProviders(value any) error { if _, ok := existing[c.Name]; ok { return validation.Errors{ strconv.Itoa(i): validation.Errors{ - "name": validation.NewError("validation_duplicated_provider", "The provider "+c.Name+" is already registered."). + "name": validation.NewError("validation_duplicated_provider", "The provider {{.name}} is already registered."). SetParams(map[string]any{"name": c.Name}), }, } @@ -493,7 +493,7 @@ func checkProviderName(value any) error { } if _, err := auth.NewProviderByName(name); err != nil { - return validation.NewError("validation_missing_provider", "Invalid or missing provider with name "+name+"."). + return validation.NewError("validation_missing_provider", "Invalid or missing provider with name {{.name}}."). SetParams(map[string]any{"name": name}) } diff --git a/core/collection_record_table_sync.go b/core/collection_record_table_sync.go index 7eda8a82..6a42ae11 100644 --- a/core/collection_record_table_sync.go +++ b/core/collection_record_table_sync.go @@ -349,9 +349,7 @@ func createCollectionIndexes(app App, collection *Collection) error { errs[strconv.Itoa(i)] = validation.NewError( "validation_invalid_index_expression", fmt.Sprintf("Failed to create index %s - %v.", parsed.IndexName, err.Error()), - ).SetParams(map[string]any{ - "indexName": parsed.IndexName, - }) + ) continue } } diff --git a/core/collection_validate.go b/core/collection_validate.go index d4e1da65..12ad7aad 100644 --- a/core/collection_validate.go +++ b/core/collection_validate.go @@ -268,7 +268,7 @@ func (validator *collectionValidator) checkFieldDuplicates(value any) error { strconv.Itoa(i): validation.Errors{ "name": validation.NewError( "validation_duplicated_field_name", - fmt.Sprintf("Duplicated or invalid field name %q", field.GetName()), + "Duplicated or invalid field name {{.fieldName}}", ).SetParams(map[string]any{ "fieldName": field.GetName(), }), @@ -452,12 +452,12 @@ func (cv *collectionValidator) checkFieldsForUniqueIndex(value any) error { for _, name := range names { field := cv.new.Fields.GetByName(name) if field == nil { - return validation.NewError("validation_missing_field", fmt.Sprintf("Invalid or missing field %q", name)). + return validation.NewError("validation_missing_field", "Invalid or missing field {{.fieldName}}"). SetParams(map[string]any{"fieldName": name}) } if !dbutils.HasSingleColumnUniqueIndex(name, cv.new.Indexes) { - return validation.NewError("validation_missing_unique_constraint", fmt.Sprintf("The field %q doesn't have a UNIQUE constraint.", name)). + return validation.NewError("validation_missing_unique_constraint", "The field {{.fieldName}} doesn't have a UNIQUE constraint."). SetParams(map[string]any{"fieldName": name}) } } @@ -566,7 +566,7 @@ func (cv *collectionValidator) checkIndexes(value any) error { return validation.Errors{ strconv.Itoa(i): validation.NewError( "validation_existing_index_name", - "The index name is already used in "+usedTblName+" collection.", + "The index name is already used in {{.usedTableName}} collection.", ).SetParams(map[string]any{"usedTableName": usedTblName}), } } @@ -649,7 +649,7 @@ func (cv *collectionValidator) checkIndexes(value any) error { if !hasMatch { return validation.NewError( "validation_invalid_unique_system_field_index", - fmt.Sprintf("Unique index definition on system fields (%q) is invalid or missing.", f.GetName()), + "Unique index definition on system fields ({{.fieldName}}) is invalid or missing.", ).SetParams(map[string]any{"fieldName": f.GetName()}) } @@ -669,7 +669,7 @@ func (cv *collectionValidator) checkIndexes(value any) error { if !dbutils.HasSingleColumnUniqueIndex(name, indexes) { return validation.NewError( "validation_missing_required_unique_index", - `Missing required unique index for field "`+name+`".`, + `Missing required unique index for field "{{.fieldName}}".`, ).SetParams(map[string]any{"fieldName": name}) } } diff --git a/core/db.go b/core/db.go index d63fe028..0aee4291 100644 --- a/core/db.go +++ b/core/db.go @@ -464,7 +464,7 @@ func validateCollectionId(app App, optTypes ...string) validation.RuleFunc { return validation.NewError( "validation_invalid_collection_type", fmt.Sprintf("Invalid collection type - must be %s.", strings.Join(optTypes, ", ")), - ).SetParams(map[string]any{"types": optTypes}) + ) } return nil diff --git a/core/field_editor.go b/core/field_editor.go index 5d3738fc..1a023a82 100644 --- a/core/field_editor.go +++ b/core/field_editor.go @@ -2,7 +2,6 @@ package core import ( "context" - "fmt" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/pocketbase/pocketbase/core/validators" @@ -137,7 +136,7 @@ func (f *EditorField) ValidateValue(ctx context.Context, app App, record *Record if int64(len(val)) > maxSize { return validation.NewError( "validation_content_size_limit", - fmt.Sprintf("The maximum allowed content size is %v bytes", maxSize), + "The maximum allowed content size is {{.maxSize}} bytes", ).SetParams(map[string]any{"maxSize": maxSize}) } diff --git a/core/field_file.go b/core/field_file.go index ec3ea269..2ef1b4e1 100644 --- a/core/field_file.go +++ b/core/field_file.go @@ -250,13 +250,22 @@ func (f *FileField) ValidateValue(ctx context.Context, app App, record *Record) addedStrings := f.excludeFiles(existingStrings, oldExistingStrings) if len(addedStrings) > 0 { - return validation.NewError("validation_invalid_file", "Invalid files:"+strings.Join(cast.ToStringSlice(addedStrings), ", ")). - SetParams(map[string]any{"invalidFiles": addedStrings}) + invalidFiles := make([]string, len(addedStrings)) + for i, invalid := range addedStrings { + invalidStr := cast.ToString(invalid) + if len(invalidStr) > 250 { + invalidStr = invalidStr[:250] + } + invalidFiles[i] = invalidStr + } + + return validation.NewError("validation_invalid_file", "Invalid new files: {{.invalidFiles}}."). + SetParams(map[string]any{"invalidFiles": invalidFiles}) } maxSelect := f.maxSelect() if len(files) > maxSelect { - return validation.NewError("validation_too_many_files", fmt.Sprintf("The maximum allowed files is %d", maxSelect)). + return validation.NewError("validation_too_many_files", "The maximum allowed files is {{.maxSelect}}"). SetParams(map[string]any{"maxSelect": maxSelect}) } diff --git a/core/field_json.go b/core/field_json.go index 27f047c8..a72786af 100644 --- a/core/field_json.go +++ b/core/field_json.go @@ -2,7 +2,6 @@ package core import ( "context" - "fmt" "slices" "strconv" "strings" @@ -160,7 +159,7 @@ func (f *JSONField) ValidateValue(ctx context.Context, app App, record *Record) if int64(len(raw)) > maxSize { return validation.NewError( "validation_json_size_limit", - fmt.Sprintf("The maximum allowed JSON size is %v bytes", maxSize), + "The maximum allowed JSON size is {{.maxSize}} bytes", ).SetParams(map[string]any{"maxSize": maxSize}) } diff --git a/core/field_relation.go b/core/field_relation.go index 3c903000..54d6f350 100644 --- a/core/field_relation.go +++ b/core/field_relation.go @@ -3,7 +3,6 @@ package core import ( "context" "database/sql/driver" - "fmt" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/pocketbase/dbx" @@ -202,13 +201,13 @@ func (f *RelationField) ValidateValue(ctx context.Context, app App, record *Reco } if f.MinSelect > 0 && len(ids) < f.MinSelect { - return validation.NewError("validation_not_enough_values", fmt.Sprintf("Select at least %d", f.MinSelect)). + return validation.NewError("validation_not_enough_values", "Select at least {{.minSelect}}"). SetParams(map[string]any{"minSelect": f.MinSelect}) } maxSelect := max(f.MaxSelect, 1) if len(ids) > maxSelect { - return validation.NewError("validation_too_many_values", fmt.Sprintf("Select no more than %d", maxSelect)). + return validation.NewError("validation_too_many_values", "Select no more than {{.maxSelect}}"). SetParams(map[string]any{"maxSelect": maxSelect}) } diff --git a/core/field_select.go b/core/field_select.go index 07744088..e77eb38c 100644 --- a/core/field_select.go +++ b/core/field_select.go @@ -3,7 +3,6 @@ package core import ( "context" "database/sql/driver" - "fmt" "slices" validation "github.com/go-ozzo/ozzo-validation/v4" @@ -192,14 +191,14 @@ func (f *SelectField) ValidateValue(ctx context.Context, app App, record *Record // check max selected items if len(normalizedVal) > maxSelect { - return validation.NewError("validation_too_many_values", fmt.Sprintf("Select no more than %d", maxSelect)). + return validation.NewError("validation_too_many_values", "Select no more than {{.maxSelect}}"). SetParams(map[string]any{"maxSelect": maxSelect}) } // check against the allowed values for _, val := range normalizedVal { if !slices.Contains(f.Values, val) { - return validation.NewError("validation_invalid_value", "Invalid value "+val). + return validation.NewError("validation_invalid_value", "Invalid value {{.value}}"). SetParams(map[string]any{"value": val}) } } diff --git a/core/field_text.go b/core/field_text.go index bb49274a..7dc77a76 100644 --- a/core/field_text.go +++ b/core/field_text.go @@ -222,7 +222,7 @@ func (f *TextField) ValidatePlainValue(value string) error { length := len([]rune(value)) if f.Min > 0 && length < f.Min { - return validation.NewError("validation_min_text_constraint", fmt.Sprintf("Must be at least %d character(s)", f.Min)). + return validation.NewError("validation_min_text_constraint", "Must be at least {{.min}} character(s)"). SetParams(map[string]any{"min": f.Min}) } @@ -232,7 +232,7 @@ func (f *TextField) ValidatePlainValue(value string) error { } if max > 0 && length > max { - return validation.NewError("validation_max_text_constraint", fmt.Sprintf("Must be less than %d character(s)", max)). + return validation.NewError("validation_max_text_constraint", "Must be less than {{.max}} character(s)"). SetParams(map[string]any{"max": f.Max}) } diff --git a/core/settings_model.go b/core/settings_model.go index fd05b96e..df68cd82 100644 --- a/core/settings_model.go +++ b/core/settings_model.go @@ -639,7 +639,7 @@ func checkUniqueRuleLabel(value any) error { if conflicts { return validation.Errors{ strconv.Itoa(i): validation.Errors{ - "label": validation.NewError("validation_conflicting_rate_limit_rule", "Rate limit rule configuration with label "+rule.Label+" already exists or conflicts with another rule."). + "label": validation.NewError("validation_conflicting_rate_limit_rule", "Rate limit rule configuration with label {{.label}} already exists or conflicts with another rule."). SetParams(map[string]any{"label": rule.Label}), }, } diff --git a/core/validators/file.go b/core/validators/file.go index 9e008c11..e3e556e9 100644 --- a/core/validators/file.go +++ b/core/validators/file.go @@ -29,7 +29,7 @@ func UploadedFileSize(maxBytes int64) validation.RuleFunc { if v.Size > maxBytes { return validation.NewError( "validation_file_size_limit", - fmt.Sprintf("Failed to upload %q - the maximum allowed file size is %v bytes.", v.OriginalName, maxBytes), + "Failed to upload {{.file}} - the maximum allowed file size is {{.maxSize}} bytes.", ).SetParams(map[string]any{ "file": v.OriginalName, "maxSize": maxBytes,