Skip to content

Commit 2389eb7

Browse files
authored
Merge pull request #16 from Azure/haitao/split_files
split files, improve code coverage
2 parents 870e93a + bb40d6c commit 2389eb7

14 files changed

+509
-341
lines changed

error.go

+35-5
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,46 @@ import (
88
type JobErrorCode string
99

1010
const (
11-
ErrPrecedentStepFailure JobErrorCode = "precedent step failed"
12-
ErrStepFailed JobErrorCode = "step failed"
13-
ErrRefStepNotInJob JobErrorCode = "trying to reference to a step not registered in job"
14-
ErrAddStepInSealedJob JobErrorCode = "trying to add step to a sealed job definition"
11+
ErrPrecedentStepFailed JobErrorCode = "PrecedentStepFailed"
12+
ErrStepFailed JobErrorCode = "StepFailed"
13+
14+
ErrRefStepNotInJob JobErrorCode = "RefStepNotInJob"
15+
MsgRefStepNotInJob string = "trying to reference to step %q, but it is not registered in job"
16+
17+
ErrAddStepInSealedJob JobErrorCode = "AddStepInSealedJob"
18+
MsgAddStepInSealedJob string = "trying to add step %q to a sealed job definition"
19+
20+
ErrAddExistingStep JobErrorCode = "AddExistingStep"
21+
MsgAddExistingStep string = "trying to add step %q to job definition, but it already exists"
22+
23+
ErrDuplicateInputParentStep JobErrorCode = "DuplicateInputParentStep"
24+
MsgDuplicateInputParentStep string = "at least 2 input parentSteps are same"
25+
26+
ErrRuntimeStepNotFound JobErrorCode = "RuntimeStepNotFound"
27+
MsgRuntimeStepNotFound string = "runtime step %q not found, must be a bug in asyncjob"
1528
)
1629

1730
func (code JobErrorCode) Error() string {
1831
return string(code)
1932
}
2033

34+
func (code JobErrorCode) WithMessage(msg string) *MessageError {
35+
return &MessageError{Code: code, Message: msg}
36+
}
37+
38+
type MessageError struct {
39+
Code JobErrorCode
40+
Message string
41+
}
42+
43+
func (me *MessageError) Error() string {
44+
return me.Code.Error() + ": " + me.Message
45+
}
46+
47+
func (me *MessageError) Unwrap() error {
48+
return me.Code
49+
}
50+
2151
type JobError struct {
2252
Code JobErrorCode
2353
StepError error
@@ -48,7 +78,7 @@ func (je *JobError) RootCause() error {
4878
}
4979

5080
// precendent step failure, track to the root
51-
if je.Code == ErrPrecedentStepFailure {
81+
if je.Code == ErrPrecedentStepFailed {
5282
precedentStepErr := &JobError{}
5383
if !errors.As(je.StepError, &precedentStepErr) {
5484
return je.StepError

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ require (
1010

1111
require (
1212
github.com/davecgh/go-spew v1.1.1 // indirect
13+
github.com/google/uuid v1.3.0 // indirect
1314
github.com/pmezard/go-difflib v1.0.0 // indirect
1415
gopkg.in/yaml.v3 v3.0.1 // indirect
1516
)

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ github.com/Azure/go-asynctask v1.3.1/go.mod h1:S1Ee5SVnt6ZUJ84brodPiHvoNfN2wgDyV
55
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
66
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
9+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
810
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
911
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1012
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=

job_definition.go

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package asyncjob
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
8+
"github.com/Azure/go-asyncjob/graph"
9+
)
10+
11+
// Interface for a job definition
12+
type JobDefinitionMeta interface {
13+
GetName() string
14+
GetStep(stepName string) (StepDefinitionMeta, bool) // TODO: switch bool to error
15+
Seal()
16+
Sealed() bool
17+
Visualize() (string, error)
18+
19+
// not exposing for now.
20+
addStep(step StepDefinitionMeta, precedingSteps ...StepDefinitionMeta) error
21+
getRootStep() StepDefinitionMeta
22+
}
23+
24+
// JobDefinition defines a job with child steps, and step is organized in a Directed Acyclic Graph (DAG).
25+
type JobDefinition[T any] struct {
26+
name string
27+
28+
sealed bool
29+
steps map[string]StepDefinitionMeta
30+
stepsDag *graph.Graph[StepDefinitionMeta]
31+
rootStep *StepDefinition[T]
32+
}
33+
34+
// Create new JobDefinition
35+
// it is suggest to build jobDefinition statically on process start, and reuse it for each job instance.
36+
func NewJobDefinition[T any](name string) *JobDefinition[T] {
37+
j := &JobDefinition[T]{
38+
name: name,
39+
steps: make(map[string]StepDefinitionMeta),
40+
stepsDag: graph.NewGraph[StepDefinitionMeta](connectStepDefinition),
41+
}
42+
43+
rootStep := newStepDefinition[T](name, stepTypeRoot)
44+
j.rootStep = rootStep
45+
46+
j.steps[j.rootStep.GetName()] = j.rootStep
47+
j.stepsDag.AddNode(j.rootStep)
48+
49+
return j
50+
}
51+
52+
// Start execution of the job definition.
53+
// this will create and return new instance of the job
54+
// caller will then be able to wait for the job instance
55+
func (jd *JobDefinition[T]) Start(ctx context.Context, input *T, jobOptions ...JobOptionPreparer) *JobInstance[T] {
56+
if !jd.Sealed() {
57+
jd.Seal()
58+
}
59+
60+
ji := newJobInstance(jd, input, jobOptions...)
61+
ji.start(ctx)
62+
63+
return ji
64+
}
65+
66+
func (jd *JobDefinition[T]) getRootStep() StepDefinitionMeta {
67+
return jd.rootStep
68+
}
69+
70+
func (jd *JobDefinition[T]) GetName() string {
71+
return jd.name
72+
}
73+
74+
func (jd *JobDefinition[T]) Seal() {
75+
if jd.sealed {
76+
return
77+
}
78+
jd.sealed = true
79+
}
80+
81+
func (jd *JobDefinition[T]) Sealed() bool {
82+
return jd.sealed
83+
}
84+
85+
// GetStep returns the stepDefinition by name
86+
func (jd *JobDefinition[T]) GetStep(stepName string) (StepDefinitionMeta, bool) {
87+
stepMeta, ok := jd.steps[stepName]
88+
return stepMeta, ok
89+
}
90+
91+
// AddStep adds a step to the job definition, with optional preceding steps
92+
func (jd *JobDefinition[T]) addStep(step StepDefinitionMeta, precedingSteps ...StepDefinitionMeta) error {
93+
jd.steps[step.GetName()] = step
94+
jd.stepsDag.AddNode(step)
95+
for _, precedingStep := range precedingSteps {
96+
if err := jd.stepsDag.Connect(precedingStep, step); err != nil {
97+
if errors.Is(err, graph.ErrConnectNotExistingNode) {
98+
return ErrRefStepNotInJob.WithMessage(fmt.Sprintf("referenced step %s not found", precedingStep.GetName()))
99+
}
100+
101+
return err
102+
}
103+
}
104+
105+
return nil
106+
}
107+
108+
// Visualize the job definition in graphviz dot format
109+
func (jd *JobDefinition[T]) Visualize() (string, error) {
110+
return jd.stepsDag.ToDotGraph()
111+
}

job.go job_instance.go

+7-96
Original file line numberDiff line numberDiff line change
@@ -7,111 +7,18 @@ import (
77

88
"github.com/Azure/go-asyncjob/graph"
99
"github.com/Azure/go-asynctask"
10+
"github.com/google/uuid"
1011
)
1112

12-
// Interface for a job definition
13-
type JobDefinitionMeta interface {
14-
GetName() string
15-
GetStep(stepName string) (StepDefinitionMeta, bool) // TODO: switch bool to error
16-
Seal()
17-
Sealed() bool
18-
19-
// not exposing for now.
20-
addStep(step StepDefinitionMeta, precedingSteps ...StepDefinitionMeta)
21-
getRootStep() StepDefinitionMeta
22-
}
23-
24-
// JobDefinition defines a job with child steps, and step is organized in a Directed Acyclic Graph (DAG).
25-
type JobDefinition[T any] struct {
26-
name string
27-
28-
sealed bool
29-
steps map[string]StepDefinitionMeta
30-
stepsDag *graph.Graph[StepDefinitionMeta]
31-
rootStep *StepDefinition[T]
32-
}
33-
34-
// Create new JobDefinition
35-
// it is suggest to build jobDefinition statically on process start, and reuse it for each job instance.
36-
func NewJobDefinition[T any](name string) *JobDefinition[T] {
37-
j := &JobDefinition[T]{
38-
name: name,
39-
steps: make(map[string]StepDefinitionMeta),
40-
stepsDag: graph.NewGraph[StepDefinitionMeta](connectStepDefinition),
41-
}
42-
43-
rootStep := newStepDefinition[T](name, stepTypeRoot)
44-
j.rootStep = rootStep
45-
46-
j.steps[j.rootStep.GetName()] = j.rootStep
47-
j.stepsDag.AddNode(j.rootStep)
48-
49-
return j
50-
}
51-
52-
// Start execution of the job definition.
53-
// this will create and return new instance of the job
54-
// caller will then be able to wait for the job instance
55-
func (jd *JobDefinition[T]) Start(ctx context.Context, input *T, jobOptions ...JobOptionPreparer) *JobInstance[T] {
56-
if !jd.Sealed() {
57-
jd.Seal()
58-
}
59-
60-
ji := newJobInstance(jd, input, jobOptions...)
61-
ji.start(ctx)
62-
63-
return ji
64-
}
65-
66-
func (jd *JobDefinition[T]) getRootStep() StepDefinitionMeta {
67-
return jd.rootStep
68-
}
69-
70-
func (jd *JobDefinition[T]) GetName() string {
71-
return jd.name
72-
}
73-
74-
func (jd *JobDefinition[T]) Seal() {
75-
if jd.sealed {
76-
return
77-
}
78-
jd.sealed = true
79-
}
80-
81-
func (jd *JobDefinition[T]) Sealed() bool {
82-
return jd.sealed
83-
}
84-
85-
// GetStep returns the stepDefinition by name
86-
func (jd *JobDefinition[T]) GetStep(stepName string) (StepDefinitionMeta, bool) {
87-
stepMeta, ok := jd.steps[stepName]
88-
return stepMeta, ok
89-
}
90-
91-
// AddStep adds a step to the job definition, with optional preceding steps
92-
func (jd *JobDefinition[T]) addStep(step StepDefinitionMeta, precedingSteps ...StepDefinitionMeta) {
93-
jd.steps[step.GetName()] = step
94-
jd.stepsDag.AddNode(step)
95-
for _, precedingStep := range precedingSteps {
96-
jd.stepsDag.Connect(precedingStep, step)
97-
}
98-
}
99-
100-
// Visualize the job definition in graphviz dot format
101-
func (jd *JobDefinition[T]) Visualize() (string, error) {
102-
return jd.stepsDag.ToDotGraph()
103-
}
104-
10513
type JobInstanceMeta interface {
14+
GetJobInstanceId() string
10615
GetJobDefinition() JobDefinitionMeta
10716
GetStepInstance(stepName string) (StepInstanceMeta, bool)
10817
Wait(context.Context) error
18+
Visualize() (string, error)
10919

11020
// not exposing for now
11121
addStepInstance(step StepInstanceMeta, precedingSteps ...StepInstanceMeta)
112-
113-
// future considering:
114-
// - return result of given step
11522
}
11623

11724
type JobExecutionOptions struct {
@@ -159,6 +66,10 @@ func newJobInstance[T any](jd *JobDefinition[T], input *T, jobInstanceOptions ..
15966
ji.jobOptions = decorator(ji.jobOptions)
16067
}
16168

69+
if ji.jobOptions.Id == "" {
70+
ji.jobOptions.Id = uuid.New().String()
71+
}
72+
16273
return ji
16374
}
16475

job_result.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ type JobInstanceWithResult[Tin, Tout any] struct {
2626
resultStep *StepInstance[Tout]
2727
}
2828

29-
func (jd *JobDefinitionWithResult[Tin, Tout]) Start(ctx context.Context, input *Tin) *JobInstanceWithResult[Tin, Tout] {
30-
ji := jd.JobDefinition.Start(ctx, input)
29+
func (jd *JobDefinitionWithResult[Tin, Tout]) Start(ctx context.Context, input *Tin, jobOptions ...JobOptionPreparer) *JobInstanceWithResult[Tin, Tout] {
30+
ji := jd.JobDefinition.Start(ctx, input, jobOptions...)
3131

3232
return &JobInstanceWithResult[Tin, Tout]{
3333
JobInstance: ji,

job_result_test.go

-29
This file was deleted.

0 commit comments

Comments
 (0)