3 Commits

Author SHA1 Message Date
9ce3ac8174 fix(xerrors): rename MarshalJSON field platformCode → platform_code (#3)
JSON tag on the anonymous struct inside MarshalJSON corrected from
`json:"platformCode,omitempty"` to `json:"platform_code,omitempty"` to
match the OpenAPI spec snake_case convention. All error responses that
serialize *Err directly now emit "platform_code" instead of "platformCode".
PR.md added to .gitignore.

Reviewed-on: #3
Co-authored-by: Rene Nochebuena Guerrero <rene@nochebuena.dev>
Co-committed-by: Rene Nochebuena Guerrero <rene@nochebuena.dev>
2026-05-21 19:30:11 -06:00
55c038f1b8 chore: bump go directive from 1.25 to 1.26 2026-05-12 02:06:45 +00:00
c6ff8d0a3f feat(xerrors)!: promote to v1.0.0 — add Unauthorized and PermissionDenied constructors
Add Unauthorized and PermissionDenied convenience constructors to complete the
set of the five most-used error codes (InvalidInput, NotFound, Internal,
Unauthorized, PermissionDenied). All roadmap items from v0.9.0 resolved.
API committed as stable.
2026-05-11 17:49:52 -06:00
5 changed files with 68 additions and 8 deletions

1
.gitignore vendored
View File

@@ -36,3 +36,4 @@ Thumbs.db
# VCS files
COMMIT.md
RELEASE.md
PR.md

View File

@@ -5,6 +5,33 @@ All notable changes to this module will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.0.2] - 2026-05-22
### Fixed
- `MarshalJSON` JSON tag corrected from `"platformCode"` to `"platform_code"` to
match the OpenAPI spec snake_case convention.
[1.0.2]: https://code.nochebuena.dev/go/xerrors/compare/v1.0.1...v1.0.2
## [1.0.0] — 2026-05-08
### Added
- `Unauthorized(msg string, args ...any) *Err` — convenience constructor for
`ErrUnauthorized`; completes the set of the five most-used codes alongside
`InvalidInput`, `NotFound`, `Internal`, and `PermissionDenied`
- `PermissionDenied(msg string, args ...any) *Err` — convenience constructor for
`ErrPermissionDenied`
### Unchanged
All existing API (`Code`, `Err`, `New`, `Wrap`, `InvalidInput`, `NotFound`,
`Internal`, `WithContext`, `WithError`, `WithPlatformCode`, `ErrorCode`,
`ErrorContext`, `MarshalJSON`) is API-compatible with v0.10.0.
[1.0.0]: https://code.nochebuena.dev/go/xerrors/releases/tag/v1.0.0
## [0.10.0] - 2026-03-25
### Added

2
go.mod
View File

@@ -1,3 +1,3 @@
module code.nochebuena.dev/go/xerrors
go 1.25
go 1.26

View File

@@ -67,6 +67,18 @@ func Internal(msg string, args ...any) *Err {
return New(ErrInternal, fmt.Sprintf(msg, args...))
}
// Unauthorized creates an Err with [ErrUnauthorized] code.
// msg is formatted with args using [fmt.Sprintf] rules.
func Unauthorized(msg string, args ...any) *Err {
return New(ErrUnauthorized, fmt.Sprintf(msg, args...))
}
// PermissionDenied creates an Err with [ErrPermissionDenied] code.
// msg is formatted with args using [fmt.Sprintf] rules.
func PermissionDenied(msg string, args ...any) *Err {
return New(ErrPermissionDenied, fmt.Sprintf(msg, args...))
}
// WithContext adds a key-value pair to the error's context fields and returns
// the receiver for chaining. Calling it multiple times with the same key
// overwrites the previous value.
@@ -176,12 +188,12 @@ func (e *Err) ErrorContext() map[string]any {
}
// MarshalJSON implements [json.Marshaler].
// Output: {"code":"NOT_FOUND","platformCode":"EMPLOYEE_NOT_FOUND","message":"...","fields":{...}}
// platformCode and fields are omitted when empty.
// Output: {"code":"NOT_FOUND","platform_code":"IDG-USR-0004","message":"...","fields":{...}}
// platform_code and fields are omitted when empty.
func (e *Err) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Code string `json:"code"`
PlatformCode string `json:"platformCode,omitempty"`
PlatformCode string `json:"platform_code,omitempty"`
Message string `json:"message"`
Fields map[string]any `json:"fields,omitempty"`
}{

View File

@@ -70,6 +70,26 @@ func TestConvenienceConstructors(t *testing.T) {
t.Errorf("unexpected message: %s", err.message)
}
})
t.Run("Unauthorized", func(t *testing.T) {
err := Unauthorized("token expired for %s", "uid1")
if err.code != ErrUnauthorized {
t.Errorf("expected code %s, got %s", ErrUnauthorized, err.code)
}
if err.message != "token expired for uid1" {
t.Errorf("unexpected message: %s", err.message)
}
})
t.Run("PermissionDenied", func(t *testing.T) {
err := PermissionDenied("role %s cannot delete", "viewer")
if err.code != ErrPermissionDenied {
t.Errorf("expected code %s, got %s", ErrPermissionDenied, err.code)
}
if err.message != "role viewer cannot delete" {
t.Errorf("unexpected message: %s", err.message)
}
})
}
func TestErr_Error(t *testing.T) {
@@ -292,8 +312,8 @@ func TestErr_MarshalJSON_PlatformCode(t *testing.T) {
if jsonErr = json.Unmarshal(b, &out); jsonErr != nil {
t.Fatalf("unmarshal error: %v", jsonErr)
}
if out["platformCode"] != "EMPLOYEE_NOT_FOUND" {
t.Errorf("json platformCode = %v, want EMPLOYEE_NOT_FOUND", out["platformCode"])
if out["platform_code"] != "EMPLOYEE_NOT_FOUND" {
t.Errorf("json platform_code = %v, want EMPLOYEE_NOT_FOUND", out["platform_code"])
}
})
@@ -303,8 +323,8 @@ func TestErr_MarshalJSON_PlatformCode(t *testing.T) {
if jsonErr != nil {
t.Fatalf("MarshalJSON error: %v", jsonErr)
}
if strings.Contains(string(b), "platformCode") {
t.Errorf("json should omit platformCode when empty, got: %s", b)
if strings.Contains(string(b), "platform_code") {
t.Errorf("json should omit platform_code when empty, got: %s", b)
}
})
}