-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds v0.3.0 files to v0 module (#21)
This PR reinstates v0 of the GoStep library within the v0 sub module, to ensure backwards compatibility. - Closes #20 --- This pull request introduces significant changes to the `go-steps` library, including updates to the documentation and the addition of new examples, functions, and types for better functionality and error handling. The most important changes include updates to the `README.md`, new examples demonstrating the use of the library, and the introduction of new types and constants. Documentation Updates: * [`README.md`](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L12-R12): Updated the note about breaking changes in `go-steps` v1 and provided guidance on how to continue using v0. Removed the "Help" section. [[1]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L12-R12) [[2]](diffhunk://#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5L388-L391) New Examples: * [`example/v0/dynamic-steps-example/main.go`](diffhunk://#diff-9f65a6c7c462b9aafde89f8842cea0388dfa47c29d4cdda9ed31b3c3cc803527R1-R46): Added a new example demonstrating dynamic step chaining and execution. * [`example/v0/multistep-example/main.go`](diffhunk://#diff-5998290fc2698be4434d3be135de85779aea097d9bd3167869e3ecc471273123R1-R82): Added a new example demonstrating multi-step execution with conditional next steps and error handling. New Functions and Types: * [`v0/go_step_types.go`](diffhunk://#diff-3871d5570fcc4606f51bc2b07d1a4afff1263ed57775afaca7a97f6fef8072f0R1-R31): Introduced new types (`StepName`, `StepFn`, `PossibleNextSteps`, `Step`, `stepArgChainingType`) to define steps and their configurations. * [`v0/go_steps_constants.go`](diffhunk://#diff-00c62dd19839758501e3de1e50db946f3af186f53029f5d0469c386ff0de908aR1-R30): Added constants and variables for default and maximum step attempts, and argument chaining types. Step Execution and Error Handling: * [`v0/go_steps.go`](diffhunk://#diff-d8d5992c8abe8b10e8a23990c37c33069dbec70650c4ef09eb5652b66459ebeaR1-R134): Implemented the `Execute` method for the `Step` type, including logic for step execution, argument resolution, and error handling with retries. * [`v0/go_steps_errors.go`](diffhunk://#diff-ee4342af74061ed13816e9ebb9deadca3ab123968b5fa1fef42361531a127b1cR1-R5): Added error messages for unresolved steps. Testing: * [`v0/go_steps_test.go`](diffhunk://#diff-18430ccf7ca865ff636a6222e0ce9055d20bc9324dd923322d894b408c259187R1-R126): Added tests for step argument resolution, next step resolution, and retry logic.
- Loading branch information
Showing
12 changed files
with
497 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
|
||
gosteps "github.com/TanmoySG/go-steps/v0" | ||
"github.com/TanmoySG/go-steps/example/v0/funcs" | ||
) | ||
|
||
func main() { | ||
|
||
intsToAdd := []int{1, 4, 7, 10} | ||
|
||
var step *gosteps.Step | ||
for _, val := range intsToAdd { | ||
step = addStepToChain(step, funcs.Add, []interface{}{val}) | ||
} | ||
|
||
finalOutput, err := step.Execute(1) | ||
if err != nil { | ||
fmt.Printf("error executing steps: %s, final output: [%s]\n", err, finalOutput) | ||
} | ||
|
||
fmt.Printf("Final Output: [%v]\n", finalOutput) | ||
} | ||
|
||
// step to add new next step to step-chain; basically a linked-list insertion | ||
func addStepToChain(step *gosteps.Step, stepFunc gosteps.StepFn, additionalArgs []interface{}) *gosteps.Step { | ||
temp := gosteps.Step{ | ||
Function: stepFunc, | ||
StepArgs: additionalArgs, | ||
} | ||
|
||
if step == nil { | ||
step = &temp | ||
return step | ||
} | ||
|
||
curr := step | ||
for curr.NextStep != nil { | ||
curr = curr.NextStep | ||
} | ||
|
||
curr.NextStep = &temp | ||
return step | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
package funcs | ||
|
||
import "fmt" | ||
|
||
var itr int = 1 | ||
|
||
func Add(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Adding %v\n", args) | ||
return []interface{}{args[0].(int) + args[1].(int)}, nil | ||
} | ||
|
||
func Sub(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Sub %v\n", args) | ||
return []interface{}{args[0].(int) - args[1].(int)}, nil | ||
} | ||
|
||
func Multiply(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Multiply %v\n", args) | ||
return []interface{}{args[0].(int) * args[1].(int)}, nil | ||
} | ||
|
||
func Divide(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Divide %v\n", args) | ||
return []interface{}{args[0].(int) / args[1].(int)}, nil | ||
} | ||
|
||
// Step will error 3times and return arg*30 and arg*31 on the 4th try | ||
func StepWillError3Times(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Running fake error function for arg [%v]\n", args) | ||
if itr == 3 { | ||
return []interface{}{args[0].(int) * 30, args[0].(int) * 50}, nil | ||
} | ||
|
||
itr += 1 | ||
return nil, fmt.Errorf("error to retry") | ||
} | ||
|
||
// Step will error infinitely | ||
func StepWillErrorInfinitely(args ...any) ([]interface{}, error) { | ||
fmt.Printf("Running infinite fake error function for arg [%v]\n", args) | ||
return nil, fmt.Errorf("error to retry") | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"time" | ||
|
||
gosteps "github.com/TanmoySG/go-steps/v0" | ||
"github.com/TanmoySG/go-steps/example/v0/funcs" | ||
) | ||
|
||
const ( | ||
stepMultiply = "Multiply" | ||
stepDivide = "Divide" | ||
) | ||
|
||
// reading/maintaining this is a bit tricky will add | ||
// a functional way to create this in the next version | ||
var steps = gosteps.Step{ | ||
Function: funcs.Add, | ||
StepArgs: []interface{}{2}, | ||
NextStep: &gosteps.Step{ | ||
Function: funcs.Sub, | ||
StepArgs: []interface{}{4}, | ||
NextStepResolver: nextStepResolver, | ||
PossibleNextSteps: gosteps.PossibleNextSteps{ | ||
{ | ||
Name: stepMultiply, | ||
Function: funcs.Multiply, | ||
StepArgs: []interface{}{-5}, | ||
NextStep: &gosteps.Step{ | ||
Function: funcs.Add, | ||
StepArgs: []interface{}{100}, | ||
NextStep: &gosteps.Step{ | ||
Function: funcs.StepWillError3Times, | ||
ErrorsToRetry: []error{ | ||
fmt.Errorf("error"), | ||
}, | ||
NextStep: &gosteps.Step{ | ||
Function: funcs.StepWillErrorInfinitely, | ||
ErrorsToRetry: []error{ | ||
fmt.Errorf("error"), | ||
}, | ||
NextStep: &gosteps.Step{ | ||
Function: funcs.Multiply, | ||
}, | ||
StrictErrorCheck: false, | ||
MaxAttempts: 5, // use gosteps.MaxMaxAttempts for Maximum Possible reattempts | ||
}, | ||
MaxAttempts: 5, | ||
RetrySleep: 1 * time.Second, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: stepDivide, | ||
Function: funcs.Divide, | ||
StepArgs: []interface{}{-2}, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
func main() { | ||
initArgs := []interface{}{5} | ||
finalOutput, err := steps.Execute(initArgs...) | ||
if err != nil { | ||
fmt.Printf("error executing steps: %s, final output: [%s]\n", err, finalOutput) | ||
} | ||
|
||
fmt.Printf("Final Output: [%v]\n", finalOutput) | ||
} | ||
|
||
// step resolver | ||
func nextStepResolver(args ...any) string { | ||
if args[0].(int) < 0 { | ||
fmt.Printf("StepResolver [%v]: Arguments is Negative, going with Multiply\n", args) | ||
return stepMultiply | ||
} | ||
|
||
fmt.Printf("StepResolver [%v]: Arguments is Positive, going with Divide\n", args) | ||
return stepDivide | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package v0 | ||
|
||
import "time" | ||
|
||
// StepName type defined the name of the step | ||
type StepName string | ||
|
||
// StepFn type defines the Step's Function | ||
type StepFn func(...interface{}) ([]interface{}, error) | ||
|
||
// PossibleNextSteps type is a list/array of Step objects | ||
type PossibleNextSteps []Step | ||
|
||
// Step type defines a step with all configurations for the step | ||
type Step struct { | ||
Name StepName | ||
Function StepFn | ||
UseArguments stepArgChainingType | ||
StepArgs []interface{} | ||
NextStep *Step | ||
PossibleNextSteps PossibleNextSteps | ||
NextStepResolver interface{} | ||
ErrorsToRetry []error | ||
StrictErrorCheck bool | ||
SkipRetry bool | ||
MaxAttempts int | ||
RetrySleep time.Duration | ||
} | ||
|
||
// enum type for step arguments chaining | ||
type stepArgChainingType string |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package v0 | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"time" | ||
) | ||
|
||
func (step *Step) Execute(initArgs ...any) ([]interface{}, error) { | ||
// final output for step execution | ||
var finalOutput []interface{} | ||
|
||
// initialize step output and step error | ||
var stepOutput []interface{} | ||
var stepError error | ||
|
||
// no initial step or function | ||
if step == nil || step.Function == nil { | ||
return nil, nil | ||
} | ||
|
||
// entry step | ||
var isEntryStep bool = true | ||
|
||
// step reattepts | ||
var stepReAttemptsLeft int = step.MaxAttempts | ||
|
||
for { | ||
// piping output from previous step as arguments for current step | ||
var stepArgs []interface{} | ||
|
||
// only runs for first step in step | ||
if isEntryStep { | ||
step.StepArgs = append(step.StepArgs, initArgs...) | ||
isEntryStep = false | ||
} | ||
|
||
// resolve step arguments based on step.UseArguments | ||
stepArgs = step.resolveStepArguments(stepOutput) | ||
|
||
// execute current step passing step arguments | ||
stepOutput, stepError = step.Function(stepArgs...) | ||
if stepError != nil { | ||
if !step.SkipRetry && step.shouldRetry(stepError) && stepReAttemptsLeft > 0 { | ||
// piping args as output for re-running same step | ||
stepOutput = stepArgs | ||
|
||
// decrementing re-attempts left for current run | ||
stepReAttemptsLeft -= 1 | ||
|
||
// sleep step.RetrySleep duration if set | ||
if step.RetrySleep > 0 { | ||
time.Sleep(step.RetrySleep) | ||
} | ||
|
||
continue | ||
} | ||
|
||
// skip retry as step error not retryable | ||
// return output of previous step and error | ||
return stepArgs, stepError | ||
} | ||
|
||
// no next step, this is the final step | ||
if step.NextStep == nil && step.PossibleNextSteps == nil { | ||
finalOutput = stepOutput | ||
return finalOutput, nil | ||
} | ||
|
||
// next step is dependant on conditions | ||
if step.PossibleNextSteps != nil && step.NextStepResolver != nil { | ||
nextStepName := step.NextStepResolver.(func(...interface{}) string)(stepOutput...) | ||
resolvedStep := step.resolveNextStep(StepName(nextStepName)) | ||
if resolvedStep == nil { | ||
return stepOutput, fmt.Errorf(unresolvedStepError, step.Name) | ||
} | ||
step.NextStep = resolvedStep | ||
} | ||
|
||
// set step as resolved or default nextStep | ||
step = step.NextStep | ||
|
||
// if step.MaxAttempts is not set, set default max value | ||
if step.MaxAttempts < 1 { | ||
step.MaxAttempts = DefaultMaxAttempts | ||
} | ||
|
||
// reset step re-attempts | ||
stepReAttemptsLeft = step.MaxAttempts - 1 | ||
} | ||
} | ||
|
||
// should retry for error | ||
func (step Step) shouldRetry(err error) bool { | ||
for _, errorToRetry := range step.ErrorsToRetry { | ||
if step.StrictErrorCheck && err.Error() == errorToRetry.Error() { | ||
return true | ||
} else if !step.StrictErrorCheck && strings.Contains(errorToRetry.Error(), err.Error()) { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// resolve next step by step name | ||
func (step Step) resolveNextStep(stepName StepName) *Step { | ||
for _, nextStep := range step.PossibleNextSteps { | ||
if nextStep.Name == stepName { | ||
return &nextStep | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (step Step) resolveStepArguments(previousStepReturns []interface{}) []interface{} { | ||
var resolvedStepArgs []interface{} | ||
|
||
switch step.UseArguments { | ||
case PreviousStepReturns: | ||
resolvedStepArgs = previousStepReturns | ||
case CurrentStepArgs: | ||
resolvedStepArgs = step.StepArgs | ||
case PreviousReturnsWithCurrentStepArgs: | ||
resolvedStepArgs = append(resolvedStepArgs, previousStepReturns...) | ||
resolvedStepArgs = append(resolvedStepArgs, step.StepArgs...) | ||
default: // covers UseCurrentStepArgsWithPreviousReturns too | ||
resolvedStepArgs = append(resolvedStepArgs, step.StepArgs...) | ||
resolvedStepArgs = append(resolvedStepArgs, previousStepReturns...) | ||
} | ||
|
||
return resolvedStepArgs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package v0 | ||
|
||
import ( | ||
"math" | ||
) | ||
|
||
const ( | ||
// to avoid infinite runs due to the MaxAttempts not being set, we're keeping the default attempts to 100 | ||
// if required, import and use the MaxMaxAttempts in the step.MaxAttempts field | ||
DefaultMaxAttempts = 100 | ||
|
||
// the Max value is 9223372036854775807, which is not infinite but a huge number of attempts | ||
MaxMaxAttempts = math.MaxInt | ||
) | ||
|
||
var ( | ||
// only previous step return will be passed to current step as arguments | ||
PreviousStepReturns stepArgChainingType = "PreviousStepReturns" | ||
|
||
// only current step arguments (StepArgs) will be passed to current step as arguments | ||
CurrentStepArgs stepArgChainingType = "CurrentStepArgs" | ||
|
||
// both previous step returns and current step arguments (StepArgs) will be passed | ||
// to current step as arguments - previous step returns, followed by current step args, | ||
PreviousReturnsWithCurrentStepArgs stepArgChainingType = "PreviousReturnsWithCurrentStepArgs" | ||
|
||
// both previous step returns and current step arguments (StepArgs) will be passed | ||
// to current step as arguments - current step args, followed by previous step returns | ||
CurrentStepArgsWithPreviousReturns stepArgChainingType = "CurrentStepArgsWithPreviousReturns" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package v0 | ||
|
||
var ( | ||
unresolvedStepError = "error: step [%s] is unresolved, no step found with this name." | ||
) |
Oops, something went wrong.