From 652e1d670b4782211b50ef46527cdc2da306ad2d Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Wed, 15 Nov 2023 11:30:10 +0100 Subject: [PATCH] fix: date format (pauseuntil, time schedule) results in error #29 --- persistence.go | 33 ++++++++++++++++++++ persistence_test.go | 49 ++++++++++++++++++++++++++++++ scheduler.go | 34 +++++++++++++++++++++ scheduler_test.go | 74 +++++++++++++++++++++++++++++++++++++++++++++ time-formats.go | 14 +++++++++ 5 files changed, 204 insertions(+) create mode 100644 time-formats.go diff --git a/persistence.go b/persistence.go index f464268..1921cb8 100644 --- a/persistence.go +++ b/persistence.go @@ -35,6 +35,39 @@ type JobPersistence struct { DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index" json:"-" yaml:"-"` } +// UnmarshalJSON implements the json.Unmarshaler interface. +func (jp *JobPersistence) UnmarshalJSON(data []byte) error { + // Anonymous struct for unmarshalling with custom time format + type Alias JobPersistence + aux := &struct { + PauseUntil *string `json:"pauseUntil,omitempty"` + *Alias + }{ + Alias: (*Alias)(jp), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + if aux.PauseUntil != nil { + var t time.Time + var err error + for _, format := range SupportedTimeFormats { + t, err = time.Parse(format, *aux.PauseUntil) + if err == nil { + break + } + } + if err != nil { + return err + } + jp.PauseUntil = &t + } + + return nil +} + func (jp JobPersistence) GetLogs() []JobLog { return jp.Logs } diff --git a/persistence_test.go b/persistence_test.go index 9f07022..32094c4 100644 --- a/persistence_test.go +++ b/persistence_test.go @@ -1,6 +1,7 @@ package jobqueue import ( + "encoding/json" "github.com/robfig/cron/v3" "github.com/stretchr/testify/assert" "io/ioutil" @@ -132,3 +133,51 @@ func TestReadYAMLFile(t *testing.T) { t.Errorf("Expected job with ID '1' and priority 1, got %+v", jobs) } } + +func TestJobPersistenceUnmarshalJSON(t *testing.T) { + testCases := []struct { + name string + jsonInput string + expected *time.Time + wantErr bool + }{ + { + name: "RFC3339 Format", + jsonInput: `{"pauseUntil":"2023-11-15T09:01:00Z"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01:00Z"), + wantErr: false, + }, + { + name: "Date and Time", + jsonInput: `{"pauseUntil":"2023-11-15T09:01"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01"), + wantErr: false, + }, + { + name: "Only Date", + jsonInput: `{"pauseUntil":"2023-11-15"}`, + expected: parseTimeForTesting(t, "2023-11-15"), + wantErr: false, + }, + { + name: "Invalid Format", + jsonInput: `{"pauseUntil":"15. November 2023"}`, + expected: nil, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var jp JobPersistence + err := json.Unmarshal([]byte(tc.jsonInput), &jp) + + if tc.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, jp.PauseUntil) + } + }) + } +} diff --git a/scheduler.go b/scheduler.go index 7bce73c..e40ee14 100644 --- a/scheduler.go +++ b/scheduler.go @@ -1,6 +1,7 @@ package jobqueue import ( + "encoding/json" "fmt" "github.com/robfig/cron/v3" "time" @@ -31,6 +32,39 @@ type SchedulerPersistence struct { Executed bool `yaml:"executed,omitempty" json:"executed,omitempty" gorm:"column:executed"` } +// UnmarshalJSON implements the json.Unmarshaler interface +func (sp *SchedulerPersistence) UnmarshalJSON(data []byte) error { + // Anonymous structure to avoid endless recursion + type Alias SchedulerPersistence + aux := &struct { + Time *string `json:"time,omitempty"` + *Alias + }{ + Alias: (*Alias)(sp), + } + + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + + if aux.Time != nil { + var t time.Time + var err error + for _, format := range SupportedTimeFormats { + t, err = time.Parse(format, *aux.Time) + if err == nil { + break + } + } + if err != nil { + return err + } + sp.Time = &t + } + + return nil +} + // IntervalScheduler is a scheduler that schedules a job at a fixed interval type IntervalScheduler struct { Interval time.Duration diff --git a/scheduler_test.go b/scheduler_test.go index 167931e..0ed3861 100644 --- a/scheduler_test.go +++ b/scheduler_test.go @@ -1,6 +1,7 @@ package jobqueue import ( + "encoding/json" "github.com/robfig/cron/v3" "github.com/stretchr/testify/assert" "sync/atomic" @@ -292,3 +293,76 @@ func TestTimeScheduler_BasicFunctionality(t *testing.T) { } } + +// TestSchedulerPersistenceUnmarshalJSON testet die Unmarshalling-Funktion mit verschiedenen Zeitformaten. +func TestSchedulerPersistenceUnmarshalJSON(t *testing.T) { + testCases := []struct { + name string + jsonInput string + expected *time.Time + wantErr bool + }{ + { + name: "RFC3339 Format", + jsonInput: `{"type":"Time","time":"2023-11-15T09:01:00Z"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01:00Z"), + wantErr: false, + }, + { + name: "Date and Time", + jsonInput: `{"type":"Time","time":"2023-11-15T09:01"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01"), + wantErr: false, + }, + { + name: "Date and Time with Space", + jsonInput: `{"type":"Time","time":"2023-11-15 09:01"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01"), + wantErr: false, + }, + { + name: "Date and Time with Space and Seconds", + jsonInput: `{"type":"Time","time":"2023-11-15 09:01:02"}`, + expected: parseTimeForTesting(t, "2023-11-15T09:01:02"), + wantErr: false, + }, + { + name: "Only Date", + jsonInput: `{"type":"Time","time":"2023-11-15"}`, + expected: parseTimeForTesting(t, "2023-11-15"), + wantErr: false, + }, + { + name: "Invalid Format", + jsonInput: `{"type":"Time","time":"15. November 2023"}`, + expected: nil, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var sp SchedulerPersistence + err := json.Unmarshal([]byte(tc.jsonInput), &sp) + + if tc.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.expected, sp.Time) + } + }) + } +} + +func parseTimeForTesting(t *testing.T, value string) *time.Time { + + for _, format := range SupportedTimeFormats { + parsedTime, err := time.Parse(format, value) + if err == nil { + return &parsedTime + } + } + t.Fatalf("Failed to parse time '%s' in any known format", value) + return nil +} diff --git a/time-formats.go b/time-formats.go new file mode 100644 index 0000000..cf80a68 --- /dev/null +++ b/time-formats.go @@ -0,0 +1,14 @@ +package jobqueue + +import "time" + +var SupportedTimeFormats = []string{ + time.RFC3339, + "2006-01-02T15:04:05", + "2006-01-02T15:04", + "2006-01-02T15", + "2006-01-02 15:04:05", + "2006-01-02 15:04", + "2006-01-02 15", + "2006-01-02", +} -- GitLab