From b70ec130d98200e0b24df204d09a2921cb4693ed Mon Sep 17 00:00:00 2001 From: John Roesler Date: Sat, 6 Mar 2021 12:03:00 -0600 Subject: [PATCH] allow enforcing uniqueness across tags (#136) --- example_test.go | 12 ++++++++++ gocron.go | 6 ++--- scheduler.go | 21 ++++++++++++++++++ scheduler_test.go | 56 +++++++++++++++++++++++------------------------ 4 files changed, 63 insertions(+), 32 deletions(-) diff --git a/example_test.go b/example_test.go index 74b68e50..d7d41802 100644 --- a/example_test.go +++ b/example_test.go @@ -525,6 +525,18 @@ func ExampleScheduler_Tag() { // [tag] } +func ExampleScheduler_TagsUnique() { + s := gocron.NewScheduler(time.UTC) + s.TagsUnique() + + _, _ = s.Every(1).Week().Tag("foo").Do(task) + _, err := s.Every(1).Week().Tag("foo").Do(task) + + fmt.Println(err) + // Output: + // a non-unique tag was set on the job: foo +} + func ExampleScheduler_TaskPresent() { s := gocron.NewScheduler(time.UTC) diff --git a/gocron.go b/gocron.go index 66b21768..4ab0c59e 100644 --- a/gocron.go +++ b/gocron.go @@ -24,9 +24,9 @@ var ( ErrInvalidInterval = errors.New(".Every() interval must be greater than 0") ErrInvalidIntervalType = errors.New(".Every() interval must be int, time.Duration, or string") ErrInvalidIntervalUnitsSelection = errors.New("an .Every() duration interval cannot be used with units (e.g. .Seconds())") - - ErrAtTimeNotSupported = errors.New("the At() not supported for time unit") - ErrWeekdayNotSupported = errors.New("weekday is not supported for time unit") + ErrAtTimeNotSupported = errors.New("the At() not supported for time unit") + ErrWeekdayNotSupported = errors.New("weekday is not supported for time unit") + ErrTagsUnique = func(tag string) error { return fmt.Errorf("a non-unique tag was set on the job: %s", tag) } ) func wrapOrError(toWrap error, err error) error { diff --git a/scheduler.go b/scheduler.go index a0dbcc40..79ed56ce 100644 --- a/scheduler.go +++ b/scheduler.go @@ -26,6 +26,8 @@ type Scheduler struct { time timeWrapper // wrapper around time.Time executor *executor // executes jobs passed via chan + + tags map[string]struct{} // for storing tags when unique tags is set } // NewScheduler creates a new Scheduler @@ -564,6 +566,17 @@ func (s *Scheduler) At(t string) *Scheduler { // Tag will add a tag when creating a job. func (s *Scheduler) Tag(t ...string) *Scheduler { job := s.getCurrentJob() + + if s.tags != nil { + for _, tag := range t { + if _, ok := s.tags[tag]; ok { + job.err = wrapOrError(job.err, ErrTagsUnique(tag)) + return s + } + s.tags[tag] = struct{}{} + } + } + job.tags = t return s } @@ -713,3 +726,11 @@ func (s *Scheduler) getCurrentJob() *Job { func (s *Scheduler) now() time.Time { return s.time.Now(s.Location()) } + +// TagsUnique forces job tags to be unique across the scheduler +// when adding tags with (s *Scheduler) Tag(). +// This does not enforce uniqueness on tags added via +// (j *Job) Tag() +func (s *Scheduler) TagsUnique() { + s.tags = make(map[string]struct{}) +} diff --git a/scheduler_test.go b/scheduler_test.go index 1fb0849d..b2d7db75 100644 --- a/scheduler_test.go +++ b/scheduler_test.go @@ -783,35 +783,6 @@ func TestRunJobsWithLimit(t *testing.T) { assert.Equal(t, 1, counter) }) - // this test isn't designed well and the functionality isn't working - //t.Run("change limit", func(t *testing.T) { - // semaphore := make(chan bool) - // - // s := NewScheduler(time.UTC) - // - // j, err := s.Every(1).Second().Do(func() { - // semaphore <- true - // }) - // require.NoError(t, err) - // j.LimitRunsTo(1) - // - // s.StartAsync() - // time.Sleep(1 * time.Second) - // - // j.LimitRunsTo(2) - // time.Sleep(1 * time.Second) - // - // var counter int - // select { - // case <-time.After(2 * time.Second): - // assert.Equal(t, 2, counter) - // // test passed - // case <-semaphore: - // counter++ - // require.LessOrEqual(t, counter, 2) - // } - //}) - t.Run("remove after last run", func(t *testing.T) { semaphore := make(chan bool) @@ -1087,3 +1058,30 @@ func TestScheduler_RemoveAfterLastRun(t *testing.T) { } }) } + +func TestScheduler_TagsUnique(t *testing.T) { + const ( + foo = "foo" + bar = "bar" + baz = "baz" + ) + + s := NewScheduler(time.UTC) + s.TagsUnique() + + j, err := s.Every("1s").Tag(foo, bar).Do(func() {}) + require.NoError(t, err) + + // uniqueness not enforced on jobs tagged with job.Tag() + // thus tagging the job here is allowed + j.Tag(baz) + _, err = s.Every("1s").Tag(baz).Do(func() {}) + require.NoError(t, err) + + _, err = s.Every("1s").Tag(foo).Do(func() {}) + assert.EqualError(t, err, ErrTagsUnique(foo).Error()) + + _, err = s.Every("1s").Tag(bar).Do(func() {}) + assert.EqualError(t, err, ErrTagsUnique(bar).Error()) + +}