From f97969f5e793a1c40ffef1c33e8afc55596dd0bb Mon Sep 17 00:00:00 2001 From: Lawrence Jones Date: Sat, 14 Aug 2021 19:47:04 +0100 Subject: [PATCH] ServiceError.Field *string Some errors generated by Goa are specific to fields- this commit ensures we populate `Field` of ServiceError when this is the case. An example would be when a field has a validation like `MinLength(1)` or similar, where the cause of the error is a specific part of the payload. It's often useful to retrieve this field, so that people can use this field to attach errors to the right form input, this being just one important use case. --- pkg/error.go | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/pkg/error.go b/pkg/error.go index 7c080d9071..390e9bdd04 100644 --- a/pkg/error.go +++ b/pkg/error.go @@ -16,6 +16,8 @@ type ( Name string // ID is a unique value for each occurrence of the error. ID string + // Pointer to the field that caused this error, if appropriate + Field *string // Message contains the specific error details. Message string // Is the error a timeout? @@ -75,13 +77,15 @@ func DecodePayloadError(msg string) error { // InvalidFieldTypeError is the error produced by the generated code when the // type of a payload field does not match the type defined in the design. func InvalidFieldTypeError(name string, val interface{}, expected string) error { - return PermanentError("invalid_field_type", "invalid value %#v for %q, must be a %s", val, name, expected) + return withField(name, PermanentError( + "invalid_field_type", "invalid value %#v for %q, must be a %s", val, name, expected)) } // MissingFieldError is the error produced by the generated code when a payload // is missing a required field. func MissingFieldError(name, context string) error { - return PermanentError("missing_field", "%q is missing from %s", name, context) + return withField(name, PermanentError( + "missing_field", "%q is missing from %s", name, context)) } // InvalidEnumValueError is the error produced by the generated code when the @@ -92,21 +96,24 @@ func InvalidEnumValueError(name string, val interface{}, allowed []interface{}) for i, a := range allowed { elems[i] = fmt.Sprintf("%#v", a) } - return PermanentError("invalid_enum_value", "value of %s must be one of %s but got value %#v", name, strings.Join(elems, ", "), val) + return withField(name, PermanentError( + "invalid_enum_value", "value of %s must be one of %s but got value %#v", name, strings.Join(elems, ", "), val)) } // InvalidFormatError is the error produced by the generated code when the value // of a payload field does not match the format validation defined in the // design. func InvalidFormatError(name, target string, format Format, formatError error) error { - return PermanentError("invalid_format", "%s must be formatted as a %s but got value %q, %s", name, format, target, formatError.Error()) + return withField(name, PermanentError( + "invalid_format", "%s must be formatted as a %s but got value %q, %s", name, format, target, formatError.Error())) } // InvalidPatternError is the error produced by the generated code when the // value of a payload field does not match the pattern validation defined in the // design. func InvalidPatternError(name, target string, pattern string) error { - return PermanentError("invalid_pattern", "%s must match the regexp %q but got value %q", name, pattern, target) + return withField(name, PermanentError( + "invalid_pattern", "%s must match the regexp %q but got value %q", name, pattern, target)) } // InvalidRangeError is the error produced by the generated code when the value @@ -117,7 +124,8 @@ func InvalidRangeError(name string, target interface{}, value interface{}, min b if !min { comp = "lesser or equal" } - return PermanentError("invalid_range", "%s must be %s than %d but got value %#v", name, comp, value, target) + return withField(name, PermanentError( + "invalid_range", "%s must be %s than %d but got value %#v", name, comp, value, target)) } // InvalidLengthError is the error produced by the generated code when the value @@ -128,7 +136,8 @@ func InvalidLengthError(name string, target interface{}, ln, value int, min bool if !min { comp = "lesser or equal" } - return PermanentError("invalid_length", "length of %s must be %s than %d but got value %#v (len=%d)", name, comp, value, target, ln) + return withField(name, PermanentError( + "invalid_length", "length of %s must be %s than %d but got value %#v (len=%d)", name, comp, value, target, ln)) } // NewErrorID creates a unique 8 character ID that is well suited to use as an @@ -184,6 +193,11 @@ func (s *ServiceError) Error() string { return s.Message } // ErrorName returns the error name. func (s *ServiceError) ErrorName() string { return s.Name } +func withField(field string, err *ServiceError) *ServiceError { + err.Field = &field + return err +} + func newError(name string, timeout, temporary, fault bool, format string, v ...interface{}) *ServiceError { return &ServiceError{ Name: name,