feat: add WithPlatformCode for domain-level error identity
Adds an optional PlatformCode field to *Err, decoupled from the transport-level Code. Code drives HTTP/gRPC status mapping; PlatformCode is a stable domain identifier for consuming applications (e.g. a frontend performing i18n) to map errors to localised messages. Platform codes are optional — errors without a user-actionable meaning (500s, infrastructure failures, auth rejections) carry none. Fully backwards-compatible: no existing signatures or JSON output changed for errors without a platform code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
54
xerrors.go
54
xerrors.go
@@ -13,18 +13,22 @@ import (
|
||||
// interfaces that logz uses internally to enrich log records — without either
|
||||
// package importing the other.
|
||||
//
|
||||
// Use the builder methods [Err.WithContext] and [Err.WithError] to attach
|
||||
// additional information after construction:
|
||||
// Use the builder methods [Err.WithContext], [Err.WithError], and
|
||||
// [Err.WithPlatformCode] to attach additional information after construction:
|
||||
//
|
||||
// err := xerrors.New(xerrors.ErrInvalidInput, "validation failed").
|
||||
// WithContext("field", "email").
|
||||
// WithContext("rule", "required").
|
||||
// WithError(cause)
|
||||
//
|
||||
// err := xerrors.New(xerrors.ErrNotFound, "employee not found").
|
||||
// WithPlatformCode("EMPLOYEE_NOT_FOUND")
|
||||
type Err struct {
|
||||
code Code
|
||||
message string
|
||||
err error
|
||||
fields map[string]any
|
||||
code Code
|
||||
message string
|
||||
err error
|
||||
fields map[string]any
|
||||
platformCode string
|
||||
}
|
||||
|
||||
// New creates an Err with the given code and message. No cause is set.
|
||||
@@ -80,6 +84,27 @@ func (e *Err) WithError(err error) *Err {
|
||||
return e
|
||||
}
|
||||
|
||||
// WithPlatformCode sets a platform-level error code and returns the receiver
|
||||
// for chaining. Platform codes are domain-specific identifiers (e.g.
|
||||
// "EMPLOYEE_NOT_FOUND") that operate independently of the transport-level
|
||||
// [Code]. They are intended for consuming applications — such as a frontend —
|
||||
// that need to map errors to localised user-facing messages without relying on
|
||||
// the generic transport code.
|
||||
//
|
||||
// Platform codes are optional. Errors that do not have a user-actionable
|
||||
// meaning (e.g. 500 internal errors, infrastructure failures) should not carry
|
||||
// one; the consuming application renders a generic fallback in those cases.
|
||||
func (e *Err) WithPlatformCode(code string) *Err {
|
||||
e.platformCode = code
|
||||
return e
|
||||
}
|
||||
|
||||
// PlatformCode returns the platform-level error code, or an empty string if
|
||||
// none was set.
|
||||
func (e *Err) PlatformCode() string {
|
||||
return e.platformCode
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
// Format: "INVALID_ARGUMENT: username is required → original cause"
|
||||
func (e *Err) Error() string {
|
||||
@@ -151,15 +176,18 @@ func (e *Err) ErrorContext() map[string]any {
|
||||
}
|
||||
|
||||
// MarshalJSON implements [json.Marshaler].
|
||||
// Output: {"code":"NOT_FOUND","message":"user not found","fields":{"id":"42"}}
|
||||
// Output: {"code":"NOT_FOUND","platformCode":"EMPLOYEE_NOT_FOUND","message":"...","fields":{...}}
|
||||
// platformCode and fields are omitted when empty.
|
||||
func (e *Err) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Fields map[string]any `json:"fields,omitempty"`
|
||||
Code string `json:"code"`
|
||||
PlatformCode string `json:"platformCode,omitempty"`
|
||||
Message string `json:"message"`
|
||||
Fields map[string]any `json:"fields,omitempty"`
|
||||
}{
|
||||
Code: string(e.code),
|
||||
Message: e.message,
|
||||
Fields: e.fields,
|
||||
Code: string(e.code),
|
||||
PlatformCode: e.platformCode,
|
||||
Message: e.message,
|
||||
Fields: e.fields,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user