diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd23289..f5aeffcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed `JobListCursor.UnmarshalText` rejecting valid cursors whose URL-safe base64 encoding contains `-` or `_`, so cursors produced by `MarshalText` always round-trip. [PR #1388](https://github.com/riverqueue/river/pull/1388). - Fixed SQLite job list pagination skipping or repeating jobs by formatting cursor timestamps consistently with stored timestamps. [PR #1374](https://github.com/riverqueue/river/pull/1374). - Improved PostgreSQL job listing performance when filtering by one finalized state (`completed`, `cancelled`, or `discarded`) and sorting by finalized time, including in River UI. [PR #1374](https://github.com/riverqueue/river/pull/1374). - Fixed `JobRescuer` overwriting jobs that complete, leave the running state, or are claimed again by another worker after being fetched for rescue, preserving their state, errors, metadata, and timestamps across PostgreSQL and SQLite drivers. Fixes [#1302](https://github.com/riverqueue/river/issues/1302). [PR #1373](https://github.com/riverqueue/river/pull/1373). diff --git a/job_list_params.go b/job_list_params.go index ded6e878..ddee1632 100644 --- a/job_list_params.go +++ b/job_list_params.go @@ -64,8 +64,8 @@ func jobListCursorFromJobAndParams(job *rivertype.JobRow, listParams *JobListPar // UnmarshalText implements encoding.TextUnmarshaler to decode the cursor from // a previously marshaled string. func (c *JobListCursor) UnmarshalText(text []byte) error { - dst := make([]byte, base64.StdEncoding.DecodedLen(len(text))) - n, err := base64.StdEncoding.Decode(dst, text) + dst := make([]byte, base64.URLEncoding.DecodedLen(len(text))) + n, err := base64.URLEncoding.Decode(dst, text) if err != nil { return err } diff --git a/job_list_params_test.go b/job_list_params_test.go index 5672866b..12089371 100644 --- a/job_list_params_test.go +++ b/job_list_params_test.go @@ -189,6 +189,25 @@ func Test_JobListCursor_MarshalJSON(t *testing.T) { _, err := json.Marshal(cursor) require.EqualError(t, err, "json: error calling MarshalText for type *river.JobListCursor: cursor initialized with only a job can't be marshaled; try a cursor from JobListResult instead") }) + + t.Run("URLSafeAlphabet", func(t *testing.T) { + t.Parallel() + + cursor := &JobListCursor{ + id: 1, + kind: "a~", + queue: "default", + sortField: JobListOrderByID, + } + + text, err := cursor.MarshalText() + require.NoError(t, err) + require.Contains(t, string(text), "-") + + decoded := &JobListCursor{} + require.NoError(t, decoded.UnmarshalText(text)) + require.Equal(t, cursor, decoded) + }) } func Test_JobListParams_toDBParams(t *testing.T) {