Skip to content

Commit

Permalink
add support for generic FunctionJob (#37)
Browse files Browse the repository at this point in the history
* require go 1.18 for generics

* add support for generic FunctionJob

Expand the suite of Jobs to include plain Golang
functions, including their closures.

You can create a FunctionJob like so:
```
job := quartz.NewFunctionJob(func() (int, error) {
    return 42, nil
}
// later on access results
println(job.JobStatus, job.Result)
````
  • Loading branch information
neomantra authored Nov 19, 2022
1 parent 8d78d00 commit 051119e
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 3 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type Job interface {
Implemented Jobs
- ShellJob
- CurlJob
- FunctionJob

## Cron expression format
| Field Name | Mandatory | Allowed Values | Allowed Special Characters |
Expand All @@ -81,8 +82,10 @@ sched.Start()
cronTrigger, _ := quartz.NewCronTrigger("1/5 * * * * *")
shellJob := quartz.NewShellJob("ls -la")
curlJob, _ := quartz.NewCurlJob(http.MethodGet, "http://worldclockapi.com/api/json/est/now", "", nil)
functionJob := quartz.NewFunctionJob(func() (int, error) { return 42, nil })
sched.ScheduleJob(shellJob, cronTrigger)
sched.ScheduleJob(curlJob, quartz.NewSimpleTrigger(time.Second*7))
sched.ScheduleJob(functionJob, quartz.NewSimpleTrigger(time.Second*5))
sched.Stop()
```
More code samples can be found in the examples directory.
Expand Down
3 changes: 3 additions & 0 deletions examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,18 @@ func sampleJobs(wg *sync.WaitGroup) {
fmt.Println(err)
return
}
functionJob := quartz.NewFunctionJobWithDesc("42", func() (int, error) { return 42, nil })

sched.ScheduleJob(shellJob, cronTrigger)
sched.ScheduleJob(curlJob, quartz.NewSimpleTrigger(time.Second*7))
sched.ScheduleJob(functionJob, quartz.NewSimpleTrigger(time.Second*3))

time.Sleep(time.Second * 10)

fmt.Println(sched.GetJobKeys())
fmt.Println(shellJob.Result)
fmt.Println(curlJob.Response)
fmt.Println(functionJob.Result)

time.Sleep(time.Second * 2)
sched.Stop()
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/reugn/go-quartz

go 1.13
go 1.18
64 changes: 64 additions & 0 deletions quartz/function_job.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package quartz

import (
"fmt"
)

// Function represents an argument-less function which returns a generic type R and a possible error.
type Function[R any] func() (R, error)

// FunctionJob represents a Job that invokes the passed Function, implements the quartz.Job interface.
type FunctionJob[R any] struct {
function *Function[R]
desc string
Result *R
Error error
JobStatus JobStatus
}

// NewFunctionJob returns a new FunctionJob without an explicit description
func NewFunctionJob[R any](function Function[R]) *FunctionJob[R] {
return &FunctionJob[R]{
function: &function,
desc: fmt.Sprintf("FunctionJob:%p", &function),
Result: nil,
Error: nil,
JobStatus: NA,
}
}

// NewFunctionJob returns a new FunctionJob with an explicit description
func NewFunctionJobWithDesc[R any](desc string, function Function[R]) *FunctionJob[R] {
return &FunctionJob[R]{
function: &function,
desc: desc,
Result: nil,
Error: nil,
JobStatus: NA,
}
}

// Description returns the description of the FunctionJob.
func (f *FunctionJob[R]) Description() string {
return f.desc
}

// Key returns the unique FunctionJob key.
func (f *FunctionJob[R]) Key() int {
return HashCode(fmt.Sprintf("%s:%p", f.desc, f.function))
}

// Execute is called by a Scheduler when the Trigger associated with this job fires.
// It invokes the held function, setting the results in Result and Error members.
func (f *FunctionJob[R]) Execute() {
result, err := (*f.function)()
if err != nil {
f.JobStatus = FAILURE
f.Result = nil
f.Error = err
} else {
f.JobStatus = OK
f.Error = nil
f.Result = &result
}
}
39 changes: 39 additions & 0 deletions quartz/function_job_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package quartz_test

import (
"testing"
"time"

"github.com/reugn/go-quartz/quartz"
)

func TestFunctionJob(t *testing.T) {
var n = 2
funcJob1 := quartz.NewFunctionJob(func() (string, error) {
n = n + 2
return "fired1", nil
})

funcJob2 := quartz.NewFunctionJob(func() (int, error) {
n = n + 2
return 42, nil
})

sched := quartz.NewStdScheduler()
sched.Start()
sched.ScheduleJob(funcJob1, quartz.NewRunOnceTrigger(time.Millisecond*300))
sched.ScheduleJob(funcJob2, quartz.NewRunOnceTrigger(time.Millisecond*800))
time.Sleep(time.Second)
sched.Clear()
sched.Stop()

assertEqual(t, funcJob1.JobStatus, quartz.OK)
assertNotEqual(t, funcJob1.Result, nil)
assertEqual(t, *funcJob1.Result, "fired1")

assertEqual(t, funcJob2.JobStatus, quartz.OK)
assertNotEqual(t, funcJob2.Result, nil)
assertEqual(t, *funcJob2.Result, 42)

assertEqual(t, n, 6)
}
4 changes: 2 additions & 2 deletions quartz/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package quartz
import (
"bytes"
"fmt"
"io/ioutil"
"io"
"net/http"
"os/exec"
)
Expand Down Expand Up @@ -139,7 +139,7 @@ func (cu *CurlJob) Execute() {
}

defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode >= 200 && resp.StatusCode < 400 {
cu.JobStatus = OK
} else {
Expand Down

0 comments on commit 051119e

Please sign in to comment.