diff --git a/engine/GruleEngine.go b/engine/GruleEngine.go index 1c62f62..59b7022 100755 --- a/engine/GruleEngine.go +++ b/engine/GruleEngine.go @@ -17,11 +17,12 @@ package engine import ( "context" "fmt" - "github.com/sirupsen/logrus" - "go.uber.org/zap" "sort" "time" + "github.com/sirupsen/logrus" + "go.uber.org/zap" + "github.com/hyperjumptech/grule-rule-engine/ast" "github.com/hyperjumptech/grule-rule-engine/logger" ) @@ -48,14 +49,12 @@ func SetLogger(externalLog interface{}) { case *zap.Logger: log, ok := externalLog.(*zap.Logger) if !ok { - return } entry = logger.NewZap(log) case *logrus.Logger: log, ok := externalLog.(*logrus.Logger) if !ok { - return } entry = logger.NewLogrus(log) @@ -70,7 +69,6 @@ func SetLogger(externalLog interface{}) { // NewGruleEngine will create new instance of GruleEngine struct. // It will set the max cycle to 5000 func NewGruleEngine() *GruleEngine { - return &GruleEngine{ MaxCycle: DefaultCycleCount, } @@ -85,7 +83,6 @@ type GruleEngine struct { // Execute function is the same as ExecuteWithContext(context.Background()) func (g *GruleEngine) Execute(dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) error { - return g.ExecuteWithContext(context.Background(), dataCtx, knowledge) } @@ -98,6 +95,24 @@ func (g *GruleEngine) notifyEvaluateRuleEntry(cycle uint64, entry *ast.RuleEntry } } +// notifyOnEvaluateRuleEntryErr() will notify all registered listener that a rule is being evaluated. +func (g *GruleEngine) notifyOnEvaluateRuleEntryErr(cycle uint64, entry *ast.RuleEntry, err error) { + if g.Listeners != nil && len(g.Listeners) > 0 { + for _, gl := range g.Listeners { + gl.OnEvaluateRuleEntryErr(cycle, entry, err) + } + } +} + +// notifyOnEvaluateRuleEntryErr() will notify all registered listener that a rule is being evaluated. +func (g *GruleEngine) notifyOnExecuteRuleEntryErr(cycle uint64, entry *ast.RuleEntry, err error) { + if g.Listeners != nil && len(g.Listeners) > 0 { + for _, gl := range g.Listeners { + gl.OnExecuteRuleEntryErr(cycle, entry, err) + } + } +} + // notifyEvaluateRuleEntry will notify all registered listener that a rule is being executed. func (g *GruleEngine) notifyExecuteRuleEntry(cycle uint64, entry *ast.RuleEntry) { if g.Listeners != nil && len(g.Listeners) > 0 { @@ -121,7 +136,6 @@ func (g *GruleEngine) notifyBeginCycle(cycle uint64) { // The engine also do conflict resolution of which rule to execute. func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) error { if knowledge == nil || dataCtx == nil { - return fmt.Errorf("nil KnowledgeBase or DataContext is not allowed") } @@ -176,9 +190,9 @@ func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataC // test if this rule entry v can execute. can, err := ruleEntry.Evaluate(ctx, dataCtx, knowledge.WorkingMemory) if err != nil { + g.notifyOnEvaluateRuleEntryErr(cycle+1, ruleEntry, err) log.Errorf("Failed testing condition for rule : %s. Got error %v", ruleEntry.RuleName, err) if g.ReturnErrOnFailedRuleEvaluation { - return err } } @@ -225,6 +239,7 @@ func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataC // execute the top most prioritized rule err := runner.Execute(ctx, dataCtx, knowledge.WorkingMemory) if err != nil { + g.notifyOnExecuteRuleEntryErr(cycle+1, runner, err) log.Errorf("Failed execution rule : %s. Got error %v", runner.RuleName, err) return fmt.Errorf("error while executing rule %s. got %w", runner.RuleName, err) @@ -249,7 +264,6 @@ func (g *GruleEngine) ExecuteWithContext(ctx context.Context, dataCtx ast.IDataC // Returns []*ast.RuleEntry order by salience func (g *GruleEngine) FetchMatchingRules(dataCtx ast.IDataContext, knowledge *ast.KnowledgeBase) ([]*ast.RuleEntry, error) { if knowledge == nil || dataCtx == nil { - return nil, fmt.Errorf("nil KnowledgeBase or DataContext is not allowed") } @@ -269,7 +283,7 @@ func (g *GruleEngine) FetchMatchingRules(dataCtx ast.IDataContext, knowledge *as log.Debugf("Initializing Context") knowledge.InitializeContext(dataCtx) - //Loop through all the rule entries available in the knowledge base and add to the response list if it is able to evaluate + // Loop through all the rule entries available in the knowledge base and add to the response list if it is able to evaluate // Select all rule entry that can be executed. log.Tracef("Select all rule entry that can be executed.") runnable := make([]*ast.RuleEntry, 0) @@ -292,7 +306,6 @@ func (g *GruleEngine) FetchMatchingRules(dataCtx ast.IDataContext, knowledge *as log.Debugf("Matching rules length %d.", len(runnable)) if len(runnable) > 1 { sort.SliceStable(runnable, func(i, j int) bool { - return runnable[i].Salience > runnable[j].Salience }) } diff --git a/engine/GruleEngineListener.go b/engine/GruleEngineListener.go index 1c369c6..d45f061 100755 --- a/engine/GruleEngineListener.go +++ b/engine/GruleEngineListener.go @@ -10,4 +10,8 @@ type GruleEngineListener interface { ExecuteRuleEntry(cycle uint64, entry *ast.RuleEntry) // BeginCycle will be called by the engine every time it start a new evaluation cycle BeginCycle(cycle uint64) + // OnEvaluateRuleEntryErr will be called by the engine if it encounter an error while evaluating a rule entry + OnEvaluateRuleEntryErr(cycle uint64, entry *ast.RuleEntry, err error) + // OnExecuteRuleEntryErr will be called by the engine if it encounter an error while executing a rule entry + OnExecuteRuleEntryErr(cycle uint64, entry *ast.RuleEntry, err error) } diff --git a/go.mod b/go.mod index 5066bbf..7d99861 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 go.uber.org/zap v1.25.0 - gopkg.in/src-d/go-billy.v4 v4.3.2 ) require ( diff --git a/go.sum b/go.sum index ee774ff..698b229 100644 --- a/go.sum +++ b/go.sum @@ -17,7 +17,6 @@ github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -55,7 +54,6 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= @@ -121,7 +119,6 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -159,13 +156,10 @@ golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= -gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= -gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=