diff --git a/persistence.go b/persistence.go index f46426804d3417d32112a36d7f28579d67336f66..1921cb8b438b4d96b5751010473e07681df8636f 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 9f070220138f390a64c0a3c082bd5ed988069f4b..32094c49c9502809e922099625e9b74c8ffb9898 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 7bce73cfca7588dcaa281f43a29a55752c66356c..e40ee148c0a25a9ae93225f8cd1132028afdf72a 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 167931e997cfb83df2776065e12eea2fe394c21f..0ed3861485a8f762452a90e6f2882a89513b6aa5 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 0000000000000000000000000000000000000000..cf80a689137b4a615307b852829c85f6e7f3faf3 --- /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", +}